blob: 863f40c55cdf3fd23d8af95d8365d69e4a7f2e61 [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from xosapi.orm import register_convenience_wrapper
from xosapi.convenience.service import ORMWrapperService
class ORMWrapperFabricCrossconnectService(ORMWrapperService):
""" Calling convention. Assume the subscribing service does approximately (needs some checks to see
if the methods exist before calling them) the following in its model_policy:
if not eastbound_service.validate_links(self):
eastbound_service.acquire_service_instance(self)
"""
def acquire_service_instance(self, subscriber_service_instance):
""" Given a subscriber_service_instance:
1) If there is an eligible provider_service_instance that can be used, then link to it
2) Otherwise, create a new provider_service_instance and link to it.
"""
(s_tag, switch_datapath_id, source_port) = self._get_west_fields(subscriber_service_instance)
FabricCrossconnectServiceInstance = self.stub.FabricCrossconnectServiceInstance
ServiceInstanceLink = self.stub.ServiceInstanceLink
candidates = FabricCrossconnectServiceInstance.objects.filter(owner_id=self.id,
s_tag=s_tag,
switch_datapath_id=switch_datapath_id,
source_port=source_port)
if candidates:
provider_service_instance = candidates[0]
else:
provider_service_instance = FabricCrossconnectServiceInstance(owner=self,
s_tag=s_tag,
switch_datapath_id=switch_datapath_id,
source_port=source_port)
provider_service_instance.save()
# NOTE: Lack-of-atomicity vulnerability -- provider_service_instance could be deleted before we created the
# link.
link = ServiceInstanceLink(provider_service_instance=provider_service_instance,
subscriber_service_instance=subscriber_service_instance)
link.save()
return provider_service_instance
def validate_links(self, subscriber_service_instance):
""" Validate existing links between the provider and subscriber service instances. If a valid link exists,
then return it. Return [] otherwise.
As a side-effect, delete any invalid links.
"""
# Short-cut -- if there are no subscriber links then we can skip getting all the properties.
if not subscriber_service_instance.subscribed_links.exists():
return None
(s_tag, switch_datapath_id, source_port) = self._get_west_fields(subscriber_service_instance)
matched = []
for link in subscriber_service_instance.subscribed_links.all():
if link.provider_service_instance.owner.id == self.id:
fcsi = link.provider_service_instance.leaf_model
if (fcsi.s_tag == s_tag) and (fcsi.switch_datapath_id == switch_datapath_id) and \
(fcsi.source_port == source_port):
matched.append(fcsi)
else:
link.delete()
return matched
def _get_west_fields(self, subscriber_si):
""" _get_west_fields()
Helper function to inspect westbound service instance for fields that will be used inside of
FabricCrossconnectServiceInstance.
"""
s_tag = subscriber_si.get_westbound_service_instance_properties("s_tag", include_self=True)
switch_datapath_id = subscriber_si.get_westbound_service_instance_properties(
"switch_datapath_id", include_self=True)
source_port = subscriber_si.get_westbound_service_instance_properties("switch_port", include_self=True)
if (s_tag is None):
raise Exception("Subscriber ServiceInstance %s s-tag is None" % subscriber_si.id)
if (not switch_datapath_id):
raise Exception("Subscriber ServiceInstance %s switch_datapath_id is unset" % subscriber_si.id)
if (source_port is None):
raise Exception("Subscriber ServiceInstance %s switch_port is None" % subscriber_si.id)
s_tag = int(s_tag)
source_port = int(source_port)
return (s_tag, switch_datapath_id, source_port)
register_convenience_wrapper("FabricCrossconnectService", ORMWrapperFabricCrossconnectService)