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): |
Scott Baker | c4156c3 | 2017-12-08 10:58:21 -0800 | [diff] [blame^] | 74 | def __init__(self, modelName=None, reverseFieldName=None): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 75 | self.modelName = modelName |
Scott Baker | c4156c3 | 2017-12-08 10:58:21 -0800 | [diff] [blame^] | 76 | self.reverseFieldName = reverseFieldName |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 77 | |
| 78 | class FakeField(object): |
| 79 | def __init__(self, field): |
| 80 | extensions = {} |
| 81 | |
| 82 | fk_model = field.get("fk_model", None) |
| 83 | if fk_model: |
Scott Baker | c4156c3 | 2017-12-08 10:58:21 -0800 | [diff] [blame^] | 84 | reverseFieldName = field.get("fk_reverseFieldName", None) |
| 85 | extensions["xos.foreignKey"] = FakeFieldOption(modelName=fk_model, reverseFieldName=reverseFieldName) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 86 | |
| 87 | fk_reverse = field.get("fk_reverse", None) |
| 88 | if fk_reverse: |
| 89 | extensions["xos.reverseForeignKey"] = FakeFieldOption(modelName=fk_reverse) |
| 90 | |
| 91 | self.Extensions = FakeExtensionManager(self, extensions) |
| 92 | |
| 93 | def GetOptions(self): |
| 94 | return self |
| 95 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 96 | class FakeDescriptor(object): |
| 97 | def __init__(self, objName): |
| 98 | global ContentTypeIdCounter |
| 99 | global ContentTypeMap |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 100 | self.objName = objName |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 101 | if objName in ContentTypeMap: |
| 102 | ct = ContentTypeMap[objName] |
| 103 | else: |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 104 | ct = "xos.%s" % objName.lower() |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 105 | ContentTypeMap[objName] = ct |
| 106 | self.Extensions = FakeExtensionManager(self, {"xos.contentTypeId": ct}) |
| 107 | |
| 108 | def GetOptions(self): |
| 109 | return self |
| 110 | |
| 111 | @property |
| 112 | def fields_by_name(self): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 113 | cls = globals()[self.objName] |
| 114 | fbn = {} |
| 115 | for field in cls.FIELDS: |
| 116 | fake_field = FakeField(field) |
| 117 | fbn[ field["name"] ] = fake_field |
| 118 | |
| 119 | return fbn |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 120 | |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 121 | class Controller(FakeObj): |
| 122 | FIELDS = ( {"name": "id", "default": 0}, |
| 123 | {"name": "name", "default": ""}, |
| 124 | {"name": "deployment_id", "default": 0, "fk_model": "Deployment"} |
| 125 | ) |
| 126 | |
| 127 | def __init__(self, **kwargs): |
| 128 | return super(Controller, self).__init__(self.FIELDS, **kwargs) |
| 129 | |
| 130 | DESCRIPTOR = FakeDescriptor("Controller") |
| 131 | |
| 132 | class Deployment(FakeObj): |
| 133 | FIELDS = ( {"name": "id", "default": 0}, |
| 134 | {"name": "name", "default": ""}, |
| 135 | ) |
| 136 | |
| 137 | def __init__(self, **kwargs): |
| 138 | return super(Deployment, self).__init__(self.FIELDS, **kwargs) |
| 139 | |
| 140 | DESCRIPTOR = FakeDescriptor("Controller") |
| 141 | |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 142 | class User(FakeObj): |
| 143 | FIELDS = ( {"name": "id", "default": 0}, |
| 144 | {"name": "email", "default": ""}, |
| 145 | {"name": "site_id", "default": 0, "fk_model": "Site"}, ) |
| 146 | |
| 147 | def __init__(self, **kwargs): |
| 148 | return super(User, self).__init__(self.FIELDS, **kwargs) |
| 149 | |
| 150 | DESCRIPTOR = FakeDescriptor("User") |
| 151 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 152 | class Slice(FakeObj): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 153 | FIELDS = ( {"name": "id", "default": 0}, |
| 154 | {"name": "name", "default": ""}, |
Scott Baker | c4156c3 | 2017-12-08 10:58:21 -0800 | [diff] [blame^] | 155 | {"name": "site_id", "default": 0, "fk_model": "Site", "fk_reverseFieldName": "slices"}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 156 | {"name": "service_id", "default": 0, "fk_model": "Service"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 157 | {"name": "creator_id", "default": 0, "fk_model": "User"}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 158 | {"name": "networks_ids", "default": [], "fk_reverse": "Network"}, |
| 159 | {"name": "network", "default": ""}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 160 | {"name": "leaf_model_name", "default": "Slice"} ) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 161 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 162 | def __init__(self, **kwargs): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 163 | return super(Slice, self).__init__(self.FIELDS, **kwargs) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 164 | |
| 165 | DESCRIPTOR = FakeDescriptor("Slice") |
| 166 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 167 | class Site(FakeObj): |
| 168 | FIELDS = ( {"name": "id", "default": 0}, |
| 169 | {"name": "name", "default": ""}, |
Scott Baker | c4156c3 | 2017-12-08 10:58:21 -0800 | [diff] [blame^] | 170 | {"name": "slices_ids", "default": [], "fk_reverse": "Slice"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 171 | {"name": "leaf_model_name", "default": "Site"}) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 172 | |
| 173 | def __init__(self, **kwargs): |
| 174 | return super(Site, self).__init__(self.FIELDS, **kwargs) |
| 175 | |
| 176 | DESCRIPTOR = FakeDescriptor("Site") |
| 177 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 178 | class Service(FakeObj): |
| 179 | FIELDS = ( {"name": "id", "default": 0}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 180 | {"name": "name", "default": ""}, |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 181 | {"name": "slices_ids", "default": [], "fk_reverse": "Slice"}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 182 | {"name": "leaf_model_name", "default": "Service"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 183 | |
| 184 | def __init__(self, **kwargs): |
| 185 | return super(Service, self).__init__(self.FIELDS, **kwargs) |
| 186 | |
| 187 | DESCRIPTOR = FakeDescriptor("Service") |
| 188 | |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame] | 189 | class ServiceInstance(FakeObj): |
| 190 | FIELDS = ( {"name": "id", "default": 0}, |
| 191 | {"name": "owher", "default": 0, "fk_model": "Service"}, |
| 192 | {"name": "leaf_model_name", "default": "ServiceInstance"}) |
| 193 | |
| 194 | def __init__(self, **kwargs): |
| 195 | return super(ServiceInstance, self).__init__(self.FIELDS, **kwargs) |
| 196 | |
| 197 | DESCRIPTOR = FakeDescriptor("ServiceInstance") |
| 198 | |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 199 | class ONOSService(FakeObj): |
| 200 | FIELDS = ( {"name": "id", "default": 0}, |
| 201 | {"name": "name", "default": ""}, |
| 202 | {"name": "leaf_model_name", "default": "ONOSService"}) |
| 203 | |
| 204 | BASES = ["Service"] |
| 205 | |
| 206 | def __init__(self, **kwargs): |
| 207 | return super(ONOSService, self).__init__(self.FIELDS, **kwargs) |
| 208 | |
| 209 | DESCRIPTOR = FakeDescriptor("ONOSService") |
| 210 | |
Scott Baker | 5f38c7b | 2017-08-18 10:12:49 -0700 | [diff] [blame] | 211 | class Network(FakeObj): |
| 212 | FIELDS = ( {"name": "id", "default": 0}, |
| 213 | {"name": "name", "default": ""}, |
| 214 | {"name": "owner_id", "default": 0, "fk_model": "Slice"}, |
| 215 | {"name": "template_id", "default": 0, "fk_model": "NetworkTemplate"}, |
| 216 | {"name": "controllernetworks_ids", "default": [], "fk_reverse": "ControllerNetwork"}, |
| 217 | {"name": "leaf_model_name", "default": "Network"}) |
| 218 | |
| 219 | def __init__(self, **kwargs): |
| 220 | return super(Network, self).__init__(self.FIELDS, **kwargs) |
| 221 | |
| 222 | DESCRIPTOR = FakeDescriptor("Network") |
| 223 | |
| 224 | class NetworkTemplate(FakeObj): |
| 225 | FIELDS = ( {"name": "id", "default": 0}, |
| 226 | {"name": "name", "default": ""}, |
| 227 | {"name": "vtn_kind", "default": ""}, |
| 228 | {"name": "leaf_model_name", "default": "NetworkTemplate"}) |
| 229 | |
| 230 | def __init__(self, **kwargs): |
| 231 | return super(NetworkTemplate, self).__init__(self.FIELDS, **kwargs) |
| 232 | |
| 233 | DESCRIPTOR = FakeDescriptor("NetworkTemplate") |
| 234 | |
| 235 | class ControllerNetwork(FakeObj): |
| 236 | FIELDS = ( {"name": "id", "default": 0}, |
| 237 | {"name": "network_id", "default": 0, "fk_model": "Network"}, |
| 238 | {"name": "controller_id", "default": 0, "fk_model": "Controller"}, |
| 239 | {"name": "leaf_model_name", "default": "ControllerNetwork"}) |
| 240 | |
| 241 | def __init__(self, **kwargs): |
| 242 | return super(ControllerNetwork, self).__init__(self.FIELDS, **kwargs) |
| 243 | |
| 244 | DESCRIPTOR = FakeDescriptor("ControllerNetwork") |
| 245 | |
| 246 | class NetworkSlice(FakeObj): |
| 247 | FIELDS = ( {"name": "id", "default": 0}, |
| 248 | {"name": "network_id", "default": 0, "fk_model": "Network"}, |
| 249 | {"name": "slice_id", "default": 0, "fk_model": "Slice"}, |
| 250 | {"name": "leaf_model_name", "default": "ControllerNetwork"}) |
| 251 | |
| 252 | def __init__(self, **kwargs): |
| 253 | return super(NetworkSlice, self).__init__(self.FIELDS, **kwargs) |
| 254 | |
| 255 | DESCRIPTOR = FakeDescriptor("NetworkSlice") |
| 256 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 257 | class Tag(FakeObj): |
| 258 | FIELDS = ( {"name": "id", "default": 0}, |
| 259 | {"name": "service_id", "default": None}, |
| 260 | {"name": "name", "default": ""}, |
| 261 | {"name": "value", "default": ""}, |
| 262 | {"name": "content_type", "default": None}, |
| 263 | {"name": "object_id", "default": None}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 264 | {"name": "leaf_model_name", "default": "Tag"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 265 | |
| 266 | def __init__(self, **kwargs): |
| 267 | return super(Tag, self).__init__(self.FIELDS, **kwargs) |
| 268 | |
| 269 | DESCRIPTOR = FakeDescriptor("Tag") |
| 270 | |
| 271 | class ID(FakeObj): |
| 272 | pass |
| 273 | |
| 274 | class FakeItemList(object): |
| 275 | def __init__(self, items): |
| 276 | self.items = items |
| 277 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 278 | class FakeStub(object): |
| 279 | def __init__(self): |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 280 | self.id_counter = 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 281 | self.objs = {} |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame] | 282 | 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] | 283 | setattr(self, "Get%s" % name, functools.partial(self.get, name)) |
| 284 | setattr(self, "List%s" % name, functools.partial(self.list, name)) |
| 285 | setattr(self, "Create%s" % name, functools.partial(self.create, name)) |
| 286 | setattr(self, "Delete%s" % name, functools.partial(self.delete, name)) |
| 287 | setattr(self, "Update%s" % name, functools.partial(self.update, name)) |
| 288 | |
| 289 | |
| 290 | def make_key(self, name, id): |
| 291 | return "%s:%d" % (name, id.id) |
| 292 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 293 | def get(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 294 | obj = self.objs.get(self.make_key(classname, id), None) |
| 295 | return obj |
| 296 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 297 | def list(self, classname, empty, metadata=None): |
| 298 | items = [] |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 299 | for (k,v) in self.objs.items(): |
| 300 | (this_classname, id) = k.split(":") |
| 301 | if this_classname == classname: |
| 302 | items.append(v) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 303 | return FakeItemList(items) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 304 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 305 | def create(self, classname, obj, metadata=None): |
| 306 | obj.id = self.id_counter |
| 307 | self.id_counter = self.id_counter + 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 308 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 309 | self.objs[k] = obj |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame] | 310 | |
| 311 | for base_classname in obj.BASES: |
| 312 | base_class = globals()[base_classname] |
| 313 | base_obj = base_class(id=obj.id, leaf_model_name = classname) |
| 314 | k = self.make_key(base_classname, base_obj) |
| 315 | self.objs[k] = base_obj |
| 316 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 317 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 318 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 319 | def update(self, classname, obj, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 320 | # TODO: partial update support? |
| 321 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 322 | self.objs[k] = obj |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 323 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 324 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 325 | def delete(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 326 | k = self.make_key(classname, id) |
| 327 | del self.objs[k] |
| 328 | |
| 329 | class FakeSymDb(object): |
| 330 | def __init__(self): |
| 331 | self._classes = {} |
Scott Baker | 8f36c3f | 2017-09-26 16:44:36 -0700 | [diff] [blame] | 332 | 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] | 333 | self._classes["xos.%s" % name] = globals()[name] |
| 334 | |
| 335 | |
| 336 | |