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 | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 119 | class User(FakeObj): |
| 120 | FIELDS = ( {"name": "id", "default": 0}, |
| 121 | {"name": "email", "default": ""}, |
| 122 | {"name": "site_id", "default": 0, "fk_model": "Site"}, ) |
| 123 | |
| 124 | def __init__(self, **kwargs): |
| 125 | return super(User, self).__init__(self.FIELDS, **kwargs) |
| 126 | |
| 127 | DESCRIPTOR = FakeDescriptor("User") |
| 128 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 129 | class Slice(FakeObj): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 130 | FIELDS = ( {"name": "id", "default": 0}, |
| 131 | {"name": "name", "default": ""}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 132 | {"name": "site_id", "default": 0, "fk_model": "Site"}, |
| 133 | {"name": "creator_id", "default": 0, "fk_model": "User"}, |
| 134 | {"name": "leaf_model_name", "default": "Slice"} ) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 135 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 136 | def __init__(self, **kwargs): |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 137 | return super(Slice, self).__init__(self.FIELDS, **kwargs) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 138 | |
| 139 | DESCRIPTOR = FakeDescriptor("Slice") |
| 140 | |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 141 | class Site(FakeObj): |
| 142 | FIELDS = ( {"name": "id", "default": 0}, |
| 143 | {"name": "name", "default": ""}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 144 | {"name": "slice_ids", "default": 0, "fk_reverse": "Slice"}, |
| 145 | {"name": "leaf_model_name", "default": "Site"}) |
Scott Baker | 03a163f | 2017-05-17 09:21:47 -0700 | [diff] [blame] | 146 | |
| 147 | def __init__(self, **kwargs): |
| 148 | return super(Site, self).__init__(self.FIELDS, **kwargs) |
| 149 | |
| 150 | DESCRIPTOR = FakeDescriptor("Site") |
| 151 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 152 | class Service(FakeObj): |
| 153 | FIELDS = ( {"name": "id", "default": 0}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 154 | {"name": "name", "default": ""}, |
| 155 | {"name": "leaf_model_name", "default": "Service"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 156 | |
| 157 | def __init__(self, **kwargs): |
| 158 | return super(Service, self).__init__(self.FIELDS, **kwargs) |
| 159 | |
| 160 | DESCRIPTOR = FakeDescriptor("Service") |
| 161 | |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 162 | class ONOSService(FakeObj): |
| 163 | FIELDS = ( {"name": "id", "default": 0}, |
| 164 | {"name": "name", "default": ""}, |
| 165 | {"name": "leaf_model_name", "default": "ONOSService"}) |
| 166 | |
| 167 | BASES = ["Service"] |
| 168 | |
| 169 | def __init__(self, **kwargs): |
| 170 | return super(ONOSService, self).__init__(self.FIELDS, **kwargs) |
| 171 | |
| 172 | DESCRIPTOR = FakeDescriptor("ONOSService") |
| 173 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 174 | class Tag(FakeObj): |
| 175 | FIELDS = ( {"name": "id", "default": 0}, |
| 176 | {"name": "service_id", "default": None}, |
| 177 | {"name": "name", "default": ""}, |
| 178 | {"name": "value", "default": ""}, |
| 179 | {"name": "content_type", "default": None}, |
| 180 | {"name": "object_id", "default": None}, |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 181 | {"name": "slice_ids", "default": 0, "fk_reverse": "Slice"}, |
| 182 | {"name": "leaf_model_name", "default": "Tag"}) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 183 | |
| 184 | def __init__(self, **kwargs): |
| 185 | return super(Tag, self).__init__(self.FIELDS, **kwargs) |
| 186 | |
| 187 | DESCRIPTOR = FakeDescriptor("Tag") |
| 188 | |
| 189 | class ID(FakeObj): |
| 190 | pass |
| 191 | |
| 192 | class FakeItemList(object): |
| 193 | def __init__(self, items): |
| 194 | self.items = items |
| 195 | |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 196 | class FakeStub(object): |
| 197 | def __init__(self): |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 198 | self.id_counter = 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 199 | self.objs = {} |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 200 | for name in ["Slice", "Site", "Tag", "Service", "ONOSService", "User"]: |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 201 | setattr(self, "Get%s" % name, functools.partial(self.get, name)) |
| 202 | setattr(self, "List%s" % name, functools.partial(self.list, name)) |
| 203 | setattr(self, "Create%s" % name, functools.partial(self.create, name)) |
| 204 | setattr(self, "Delete%s" % name, functools.partial(self.delete, name)) |
| 205 | setattr(self, "Update%s" % name, functools.partial(self.update, name)) |
| 206 | |
| 207 | |
| 208 | def make_key(self, name, id): |
| 209 | return "%s:%d" % (name, id.id) |
| 210 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 211 | def get(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 212 | obj = self.objs.get(self.make_key(classname, id), None) |
| 213 | return obj |
| 214 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 215 | def list(self, classname, empty, metadata=None): |
| 216 | items = [] |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 217 | for (k,v) in self.objs.items(): |
| 218 | (this_classname, id) = k.split(":") |
| 219 | if this_classname == classname: |
| 220 | items.append(v) |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 221 | return FakeItemList(items) |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 222 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 223 | def create(self, classname, obj, metadata=None): |
| 224 | obj.id = self.id_counter |
| 225 | self.id_counter = self.id_counter + 1 |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 226 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 227 | self.objs[k] = obj |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 228 | |
| 229 | for base_classname in obj.BASES: |
| 230 | base_class = globals()[base_classname] |
| 231 | base_obj = base_class(id=obj.id, leaf_model_name = classname) |
| 232 | k = self.make_key(base_classname, base_obj) |
| 233 | self.objs[k] = base_obj |
| 234 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 235 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 236 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 237 | def update(self, classname, obj, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 238 | # TODO: partial update support? |
| 239 | k = self.make_key(classname, FakeObj(id=obj.id)) |
| 240 | self.objs[k] = obj |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 241 | return obj |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 242 | |
Scott Baker | 500f8c7 | 2017-05-19 09:41:50 -0700 | [diff] [blame] | 243 | def delete(self, classname, id, metadata=None): |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 244 | k = self.make_key(classname, id) |
| 245 | del self.objs[k] |
| 246 | |
| 247 | class FakeSymDb(object): |
| 248 | def __init__(self): |
| 249 | self._classes = {} |
Scott Baker | ff104cc | 2017-08-14 15:24:41 -0700 | [diff] [blame^] | 250 | for name in ["Slice", "Site", "ID", "Tag", "Service", "ONOSService", "User"]: |
Scott Baker | f0ee0dc | 2017-05-15 10:10:05 -0700 | [diff] [blame] | 251 | self._classes["xos.%s" % name] = globals()[name] |
| 252 | |
| 253 | |
| 254 | |