Scott Baker | 252f692 | 2018-08-22 16:57:23 -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 | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 17 | from xosapi.orm import register_convenience_wrapper |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 18 | from xosapi.convenience.service import ORMWrapperService |
| 19 | |
Scott Baker | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 20 | |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 21 | class ORMWrapperFabricCrossconnectService(ORMWrapperService): |
| 22 | |
| 23 | """ Calling convention. Assume the subscribing service does approximately (needs some checks to see |
| 24 | if the methods exist before calling them) the following in its model_policy: |
| 25 | |
| 26 | if not eastbound_service.validate_links(self): |
| 27 | eastbound_service.acquire_service_instance(self) |
| 28 | """ |
| 29 | |
| 30 | def acquire_service_instance(self, subscriber_service_instance): |
| 31 | """ Given a subscriber_service_instance: |
| 32 | 1) If there is an eligible provider_service_instance that can be used, then link to it |
| 33 | 2) Otherwise, create a new provider_service_instance and link to it. |
| 34 | """ |
| 35 | (s_tag, switch_datapath_id, source_port) = self._get_west_fields(subscriber_service_instance) |
| 36 | |
| 37 | FabricCrossconnectServiceInstance = self.stub.FabricCrossconnectServiceInstance |
| 38 | ServiceInstanceLink = self.stub.ServiceInstanceLink |
| 39 | |
| 40 | candidates = FabricCrossconnectServiceInstance.objects.filter(owner_id=self.id, |
| 41 | s_tag=s_tag, |
| 42 | switch_datapath_id=switch_datapath_id, |
| 43 | source_port=source_port) |
| 44 | |
| 45 | if candidates: |
| 46 | provider_service_instance = candidates[0] |
| 47 | else: |
| 48 | provider_service_instance = FabricCrossconnectServiceInstance(owner=self, |
Scott Baker | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 49 | s_tag=s_tag, |
| 50 | switch_datapath_id=switch_datapath_id, |
| 51 | source_port=source_port) |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 52 | provider_service_instance.save() |
| 53 | |
| 54 | # NOTE: Lack-of-atomicity vulnerability -- provider_service_instance could be deleted before we created the |
| 55 | # link. |
| 56 | |
| 57 | link = ServiceInstanceLink(provider_service_instance=provider_service_instance, |
| 58 | subscriber_service_instance=subscriber_service_instance) |
| 59 | link.save() |
| 60 | |
| 61 | return provider_service_instance |
| 62 | |
| 63 | def validate_links(self, subscriber_service_instance): |
| 64 | """ Validate existing links between the provider and subscriber service instances. If a valid link exists, |
| 65 | then return it. Return [] otherwise. |
| 66 | |
| 67 | As a side-effect, delete any invalid links. |
| 68 | """ |
| 69 | |
| 70 | # Short-cut -- if there are no subscriber links then we can skip getting all the properties. |
| 71 | if not subscriber_service_instance.subscribed_links.exists(): |
| 72 | return None |
| 73 | |
| 74 | (s_tag, switch_datapath_id, source_port) = self._get_west_fields(subscriber_service_instance) |
| 75 | |
| 76 | matched = [] |
| 77 | for link in subscriber_service_instance.subscribed_links.all(): |
| 78 | if link.provider_service_instance.owner.id == self.id: |
| 79 | fcsi = link.provider_service_instance.leaf_model |
| 80 | if (fcsi.s_tag == s_tag) and (fcsi.switch_datapath_id == switch_datapath_id) and \ |
Scott Baker | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 81 | (fcsi.source_port == source_port): |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 82 | matched.append(fcsi) |
| 83 | else: |
| 84 | link.delete() |
| 85 | return matched |
| 86 | |
| 87 | def _get_west_fields(self, subscriber_si): |
| 88 | """ _get_west_fields() |
| 89 | |
| 90 | Helper function to inspect westbound service instance for fields that will be used inside of |
| 91 | FabricCrossconnectServiceInstance. |
| 92 | """ |
| 93 | |
| 94 | s_tag = subscriber_si.get_westbound_service_instance_properties("s_tag", include_self=True) |
Scott Baker | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 95 | switch_datapath_id = subscriber_si.get_westbound_service_instance_properties( |
| 96 | "switch_datapath_id", include_self=True) |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 97 | source_port = subscriber_si.get_westbound_service_instance_properties("switch_port", include_self=True) |
| 98 | |
| 99 | if (s_tag is None): |
| 100 | raise Exception("Subscriber ServiceInstance %s s-tag is None" % subscriber_si.id) |
| 101 | |
| 102 | if (not switch_datapath_id): |
| 103 | raise Exception("Subscriber ServiceInstance %s switch_datapath_id is unset" % subscriber_si.id) |
| 104 | |
| 105 | if (source_port is None): |
| 106 | raise Exception("Subscriber ServiceInstance %s switch_port is None" % subscriber_si.id) |
| 107 | |
| 108 | s_tag = int(s_tag) |
| 109 | source_port = int(source_port) |
| 110 | |
| 111 | return (s_tag, switch_datapath_id, source_port) |
| 112 | |
Scott Baker | e7b55e4 | 2019-04-01 17:18:03 -0700 | [diff] [blame] | 113 | |
Scott Baker | 252f692 | 2018-08-22 16:57:23 -0700 | [diff] [blame] | 114 | register_convenience_wrapper("FabricCrossconnectService", ORMWrapperFabricCrossconnectService) |