Matteo Scandolo | d2044a4 | 2017-08-07 16:08:28 -0700 | [diff] [blame] | 1 | |
| 2 | # Copyright 2017-present Open Networking Foundation |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | |
| 16 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 17 | """ fake_stub.py |
| 18 | |
| 19 | Implements a simple fake grpc stub to use for unit testing. |
| 20 | """ |
| 21 | |
| 22 | import functools |
| 23 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 24 | ContentTypeMap = {} |
| 25 | |
| 26 | class FakeObj(object): |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 27 | BASES=[] |
| 28 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 29 | def __init__(self, fields=[], **kwargs): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 30 | super(FakeObj, self).__setattr__("is_set", {}) |
| 31 | super(FakeObj, self).__setattr__("fields", []) |
| 32 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 33 | for f in fields: |
| 34 | name = f["name"] |
| 35 | self.fields.append(name) |
| 36 | setattr(self, name, f["default"]) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 37 | |
| 38 | super(FakeObj, self).__setattr__("is_set", {}) |
| 39 | for (k,v) in kwargs.items(): |
| 40 | setattr(self, k, v) |
| 41 | |
| 42 | def __repr__(self): |
| 43 | lines = [] |
| 44 | for k in self.fields: |
| 45 | if self.is_set.get(k, False): |
| 46 | lines.append('%s: "%s"' % (k, getattr(self, k))) |
| 47 | if lines: |
| 48 | return "\n".join(lines) + "\n" |
| 49 | else: |
| 50 | return "" |
| 51 | |
| 52 | def __setattr__(self, name, value): |
| 53 | self.is_set[name] = True |
| 54 | super(FakeObj, self).__setattr__(name, value) |
| 55 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 56 | @property |
| 57 | def self_content_type_id(self): |
| 58 | return "xos.%s" % self.__class__.__name__.lower() |
| 59 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 60 | class FakeExtensionManager(object): |
| 61 | def __init__(self, obj, extensions): |
| 62 | self.obj = obj |
| 63 | self.extensions = extensions |
| 64 | |
| 65 | def _FindExtensionByName(self, name): |
| 66 | return name |
| 67 | |
| 68 | def __getitem__(self, name, default=None): |
| 69 | if name in self.extensions: |
| 70 | return self.extensions[name] |
| 71 | return default |
| 72 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 73 | class FakeFieldOption(object): |
| 74 | def __init__(self, modelName=None): |
| 75 | self.modelName = modelName |
| 76 | |
| 77 | class FakeField(object): |
| 78 | def __init__(self, field): |
| 79 | extensions = {} |
| 80 | |
| 81 | fk_model = field.get("fk_model", None) |
| 82 | if fk_model: |
| 83 | extensions["xos.foreignKey"] = FakeFieldOption(modelName=fk_model) |
| 84 | |
| 85 | fk_reverse = field.get("fk_reverse", None) |
| 86 | if fk_reverse: |
| 87 | extensions["xos.reverseForeignKey"] = FakeFieldOption(modelName=fk_reverse) |
| 88 | |
| 89 | self.Extensions = FakeExtensionManager(self, extensions) |
| 90 | |
| 91 | def GetOptions(self): |
| 92 | return self |
| 93 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 94 | class FakeDescriptor(object): |
| 95 | def __init__(self, objName): |
| 96 | global ContentTypeIdCounter |
| 97 | global ContentTypeMap |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 98 | self.objName = objName |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 99 | if objName in ContentTypeMap: |
| 100 | ct = ContentTypeMap[objName] |
| 101 | else: |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 102 | ct = "xos.%s" % objName.lower() |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 103 | ContentTypeMap[objName] = ct |
| 104 | self.Extensions = FakeExtensionManager(self, {"xos.contentTypeId": ct}) |
| 105 | |
| 106 | def GetOptions(self): |
| 107 | return self |
| 108 | |
| 109 | @property |
| 110 | def fields_by_name(self): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 111 | cls = globals()[self.objName] |
| 112 | fbn = {} |
| 113 | for field in cls.FIELDS: |
| 114 | fake_field = FakeField(field) |
| 115 | fbn[ field["name"] ] = fake_field |
| 116 | |
| 117 | return fbn |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 118 | |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 119 | class Controller(FakeObj): |
| 120 | FIELDS = ( {"name": "id", "default": 0}, |
| 121 | {"name": "name", "default": ""}, |
| 122 | {"name": "deployment_id", "default": 0, "fk_model": "Deployment"} |
| 123 | ) |
| 124 | |
| 125 | def __init__(self, **kwargs): |
| 126 | return super(Controller, self).__init__(self.FIELDS, **kwargs) |
| 127 | |
| 128 | DESCRIPTOR = FakeDescriptor("Controller") |
| 129 | |
| 130 | class Deployment(FakeObj): |
| 131 | FIELDS = ( {"name": "id", "default": 0}, |
| 132 | {"name": "name", "default": ""}, |
| 133 | ) |
| 134 | |
| 135 | def __init__(self, **kwargs): |
| 136 | return super(Deployment, self).__init__(self.FIELDS, **kwargs) |
| 137 | |
| 138 | DESCRIPTOR = FakeDescriptor("Controller") |
| 139 | |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 140 | class User(FakeObj): |
| 141 | FIELDS = ( {"name": "id", "default": 0}, |
| 142 | {"name": "email", "default": ""}, |
| 143 | {"name": "site_id", "default": 0, "fk_model": "Site"}, ) |
| 144 | |
| 145 | def __init__(self, **kwargs): |
| 146 | return super(User, self).__init__(self.FIELDS, **kwargs) |
| 147 | |
| 148 | DESCRIPTOR = FakeDescriptor("User") |
| 149 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 150 | class Slice(FakeObj): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 151 | FIELDS = ( {"name": "id", "default": 0}, |
| 152 | {"name": "name", "default": ""}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 153 | {"name": "site_id", "default": 0, "fk_model": "Site"}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 154 | {"name": "service_id", "default": 0, "fk_model": "Service"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 155 | {"name": "creator_id", "default": 0, "fk_model": "User"}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 156 | {"name": "networks_ids", "default": [], "fk_reverse": "Network"}, |
| 157 | {"name": "network", "default": ""}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 158 | {"name": "leaf_model_name", "default": "Slice"} ) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 159 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 160 | def __init__(self, **kwargs): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 161 | return super(Slice, self).__init__(self.FIELDS, **kwargs) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 162 | |
| 163 | DESCRIPTOR = FakeDescriptor("Slice") |
| 164 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 165 | class Site(FakeObj): |
| 166 | FIELDS = ( {"name": "id", "default": 0}, |
| 167 | {"name": "name", "default": ""}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 168 | {"name": "slice_ids", "default": [], "fk_reverse": "Slice"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 169 | {"name": "leaf_model_name", "default": "Site"}) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 170 | |
| 171 | def __init__(self, **kwargs): |
| 172 | return super(Site, self).__init__(self.FIELDS, **kwargs) |
| 173 | |
| 174 | DESCRIPTOR = FakeDescriptor("Site") |
| 175 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 176 | class Service(FakeObj): |
| 177 | FIELDS = ( {"name": "id", "default": 0}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 178 | {"name": "name", "default": ""}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 179 | {"name": "slices_ids", "default": [], "fk_reverse": "Slice"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 180 | {"name": "leaf_model_name", "default": "Service"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 181 | |
| 182 | def __init__(self, **kwargs): |
| 183 | return super(Service, self).__init__(self.FIELDS, **kwargs) |
| 184 | |
| 185 | DESCRIPTOR = FakeDescriptor("Service") |
| 186 | |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame^] | 187 | class ServiceInstance(FakeObj): |
| 188 | FIELDS = ( {"name": "id", "default": 0}, |
| 189 | {"name": "owher", "default": 0, "fk_model": "Service"}, |
| 190 | {"name": "leaf_model_name", "default": "ServiceInstance"}) |
| 191 | |
| 192 | def __init__(self, **kwargs): |
| 193 | return super(ServiceInstance, self).__init__(self.FIELDS, **kwargs) |
| 194 | |
| 195 | DESCRIPTOR = FakeDescriptor("ServiceInstance") |
| 196 | |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 197 | class ONOSService(FakeObj): |
| 198 | FIELDS = ( {"name": "id", "default": 0}, |
| 199 | {"name": "name", "default": ""}, |
| 200 | {"name": "leaf_model_name", "default": "ONOSService"}) |
| 201 | |
| 202 | BASES = ["Service"] |
| 203 | |
| 204 | def __init__(self, **kwargs): |
| 205 | return super(ONOSService, self).__init__(self.FIELDS, **kwargs) |
| 206 | |
| 207 | DESCRIPTOR = FakeDescriptor("ONOSService") |
| 208 | |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 209 | class Network(FakeObj): |
| 210 | FIELDS = ( {"name": "id", "default": 0}, |
| 211 | {"name": "name", "default": ""}, |
| 212 | {"name": "owner_id", "default": 0, "fk_model": "Slice"}, |
| 213 | {"name": "template_id", "default": 0, "fk_model": "NetworkTemplate"}, |
| 214 | {"name": "controllernetworks_ids", "default": [], "fk_reverse": "ControllerNetwork"}, |
| 215 | {"name": "leaf_model_name", "default": "Network"}) |
| 216 | |
| 217 | def __init__(self, **kwargs): |
| 218 | return super(Network, self).__init__(self.FIELDS, **kwargs) |
| 219 | |
| 220 | DESCRIPTOR = FakeDescriptor("Network") |
| 221 | |
| 222 | class NetworkTemplate(FakeObj): |
| 223 | FIELDS = ( {"name": "id", "default": 0}, |
| 224 | {"name": "name", "default": ""}, |
| 225 | {"name": "vtn_kind", "default": ""}, |
| 226 | {"name": "leaf_model_name", "default": "NetworkTemplate"}) |
| 227 | |
| 228 | def __init__(self, **kwargs): |
| 229 | return super(NetworkTemplate, self).__init__(self.FIELDS, **kwargs) |
| 230 | |
| 231 | DESCRIPTOR = FakeDescriptor("NetworkTemplate") |
| 232 | |
| 233 | class ControllerNetwork(FakeObj): |
| 234 | FIELDS = ( {"name": "id", "default": 0}, |
| 235 | {"name": "network_id", "default": 0, "fk_model": "Network"}, |
| 236 | {"name": "controller_id", "default": 0, "fk_model": "Controller"}, |
| 237 | {"name": "leaf_model_name", "default": "ControllerNetwork"}) |
| 238 | |
| 239 | def __init__(self, **kwargs): |
| 240 | return super(ControllerNetwork, self).__init__(self.FIELDS, **kwargs) |
| 241 | |
| 242 | DESCRIPTOR = FakeDescriptor("ControllerNetwork") |
| 243 | |
| 244 | class NetworkSlice(FakeObj): |
| 245 | FIELDS = ( {"name": "id", "default": 0}, |
| 246 | {"name": "network_id", "default": 0, "fk_model": "Network"}, |
| 247 | {"name": "slice_id", "default": 0, "fk_model": "Slice"}, |
| 248 | {"name": "leaf_model_name", "default": "ControllerNetwork"}) |
| 249 | |
| 250 | def __init__(self, **kwargs): |
| 251 | return super(NetworkSlice, self).__init__(self.FIELDS, **kwargs) |
| 252 | |
| 253 | DESCRIPTOR = FakeDescriptor("NetworkSlice") |
| 254 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 255 | class Tag(FakeObj): |
| 256 | FIELDS = ( {"name": "id", "default": 0}, |
| 257 | {"name": "service_id", "default": None}, |
| 258 | {"name": "name", "default": ""}, |
| 259 | {"name": "value", "default": ""}, |
| 260 | {"name": "content_type", "default": None}, |
| 261 | {"name": "object_id", "default": None}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 262 | {"name": "leaf_model_name", "default": "Tag"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 263 | |
| 264 | def __init__(self, **kwargs): |
| 265 | return super(Tag, self).__init__(self.FIELDS, **kwargs) |
| 266 | |
| 267 | DESCRIPTOR = FakeDescriptor("Tag") |
| 268 | |
| 269 | class ID(FakeObj): |
| 270 | pass |
| 271 | |
| 272 | class FakeItemList(object): |
| 273 | def __init__(self, items): |
| 274 | self.items = items |
| 275 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 276 | class FakeStub(object): |
| 277 | def __init__(self): |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 278 | self.id_counter = 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 279 | self.objs = {} |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame^] | 280 | for name in ["Controller", "Deployment", "Slice", "Site", "Tag", "Service", "ServiceInstance", "ONOSService", "User", "Network", "NetworkTemplate", "ControllerNetwork", "NetworkSlice"]: |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 281 | setattr(self, "Get%s" % name, functools.partial(self.get, name)) |
| 282 | setattr(self, "List%s" % name, functools.partial(self.list, name)) |
| 283 | setattr(self, "Create%s" % name, functools.partial(self.create, name)) |
| 284 | setattr(self, "Delete%s" % name, functools.partial(self.delete, name)) |
| 285 | setattr(self, "Update%s" % name, functools.partial(self.update, name)) |
| 286 | |
| 287 | |
| 288 | def make_key(self, name, id): |
| 289 | return "%s:%d" % (name, id.id) |
| 290 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 291 | def get(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 292 | obj = self.objs.get(self.make_key(classname, id), None) |
| 293 | return obj |
| 294 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 295 | def list(self, classname, empty, metadata=None): |
| 296 | items = [] |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 297 | for (k,v) in self.objs.items(): |
| 298 | (this_classname, id) = k.split(":") |
| 299 | if this_classname == classname: |
| 300 | items.append(v) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 301 | return FakeItemList(items) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 302 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 303 | def create(self, classname, obj, metadata=None): |
| 304 | obj.id = self.id_counter |
| 305 | self.id_counter = self.id_counter + 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 306 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 307 | self.objs[k] = obj |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 308 | |
| 309 | for base_classname in obj.BASES: |
| 310 | base_class = globals()[base_classname] |
| 311 | base_obj = base_class(id=obj.id, leaf_model_name = classname) |
| 312 | k = self.make_key(base_classname, base_obj) |
| 313 | self.objs[k] = base_obj |
| 314 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 315 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 316 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 317 | def update(self, classname, obj, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 318 | # TODO: partial update support? |
| 319 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 320 | self.objs[k] = obj |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 321 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 322 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 323 | def delete(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 324 | k = self.make_key(classname, id) |
| 325 | del self.objs[k] |
| 326 | |
| 327 | class FakeSymDb(object): |
| 328 | def __init__(self): |
| 329 | self._classes = {} |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame^] | 330 | for name in ["Controller", "Deployment", "Slice", "Site", "ID", "Tag", "Service", "ServiceInstance", "ONOSService", "User", "Network", "NetworkTemplate", "ControllerNetwork", "NetworkSlice"]: |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 331 | self._classes["xos.%s" % name] = globals()[name] |
| 332 | |
| 333 | |
| 334 | |