blob: 4d8a3552696e1d53b521481241d347dee5857d95 [file] [log] [blame]
Scott Bakera6c687c2018-07-16 15:08:49 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Scott Baker63afcc12019-02-01 15:41:46 -080015from xossynchronizer.steps.syncstep import SyncStep, DeferredException
Scott Bakere7b55e42019-04-01 17:18:03 -070016from xossynchronizer.modelaccessor import model_accessor, \
17 FabricCrossconnectServiceInstance, \
18 ServiceInstance, \
19 BNGPortMapping
Scott Bakera6c687c2018-07-16 15:08:49 -070020
21from xosconfig import Config
22from multistructlog import create_logger
Scott Bakera6c687c2018-07-16 15:08:49 -070023import requests
24from requests.auth import HTTPBasicAuth
Himanshu Bhandari32738e82020-06-10 21:08:49 +053025from helpers import Helpers
Scott Bakera6c687c2018-07-16 15:08:49 -070026
27
28class SyncFabricCrossconnectServiceInstance(SyncStep):
29 provides = [FabricCrossconnectServiceInstance]
30 log = create_logger(Config().get('logging'))
31
32 observes = FabricCrossconnectServiceInstance
33
Scott Baker82565472018-08-20 11:40:03 -070034 def make_handle(self, s_tag, switch_datapath_id):
Scott Baker547dea02018-07-18 15:24:26 -070035 # Generate a backend_handle that uniquely identifies the cross connect. ONOS doesn't provide us a handle, so
36 # we make up our own. This helps us to detect other FabricCrossconnectServiceInstance using the same
37 # entry, as well as to be able to extract the necessary information to delete the entry later.
Scott Baker82565472018-08-20 11:40:03 -070038 return "%d/%s" % (s_tag, switch_datapath_id)
Scott Baker547dea02018-07-18 15:24:26 -070039
40 def extract_handle(self, backend_handle):
Scott Bakere7b55e42019-04-01 17:18:03 -070041 (s_tag, switch_datapath_id) = backend_handle.split("/", 1)
Scott Baker547dea02018-07-18 15:24:26 -070042 s_tag = int(s_tag)
Scott Baker82565472018-08-20 11:40:03 -070043 return (s_tag, switch_datapath_id)
Scott Bakera6c687c2018-07-16 15:08:49 -070044
Scott Bakerd443ea72018-08-07 13:50:06 -070045 def range_matches(self, value, pattern):
Scott Bakere7b55e42019-04-01 17:18:03 -070046 value = int(value)
Scott Bakerd443ea72018-08-07 13:50:06 -070047 for this_range in pattern.split(","):
48 this_range = this_range.strip()
49 if "-" in this_range:
50 (first, last) = this_range.split("-")
51 first = int(first.strip())
52 last = int(last.strip())
Scott Bakere7b55e42019-04-01 17:18:03 -070053 if (value >= first) and (value <= last):
Scott Bakerd443ea72018-08-07 13:50:06 -070054 return True
Scott Bakere7b55e42019-04-01 17:18:03 -070055 elif this_range.lower() == "any":
Scott Bakerd443ea72018-08-07 13:50:06 -070056 return True
57 else:
Scott Bakere7b55e42019-04-01 17:18:03 -070058 if (value == int(this_range)):
Scott Bakerd443ea72018-08-07 13:50:06 -070059 return True
60 return False
61
62 def find_bng(self, s_tag):
63 # See if there's a mapping for our s-tag directly
64 bng_mappings = BNGPortMapping.objects.filter(s_tag=str(s_tag))
65 if bng_mappings:
66 return bng_mappings[0]
67
68 # TODO(smbaker): Examine miss performance, and if necessary set a flag in the save method to allow filtering
69 # of mappings based on whether they are ranges or any.
70
71 # See if there are any ranges or "any" that match
72 for bng_mapping in BNGPortMapping.objects.all():
73 if self.range_matches(s_tag, bng_mapping.s_tag):
Scott Bakere7b55e42019-04-01 17:18:03 -070074 return bng_mapping
Scott Bakerd443ea72018-08-07 13:50:06 -070075
76 return None
77
Scott Bakera6c687c2018-07-16 15:08:49 -070078 def sync_record(self, o):
79 self.log.info("Sync'ing Fabric Crossconnect Service Instance", service_instance=o)
80
Scott Baker82565472018-08-20 11:40:03 -070081 if (o.policed is None) or (o.policed < o.updated):
82 raise DeferredException("Waiting for model_policy to run on fcsi %s" % o.id)
83
Himanshu Bhandari32738e82020-06-10 21:08:49 +053084 onos = Helpers.get_fabric_onos_info(self.model_accessor, o.owner)
Scott Bakera6c687c2018-07-16 15:08:49 -070085
Scott Bakere7b55e42019-04-01 17:18:03 -070086 ServiceInstance.objects.get(id=o.id)
Scott Bakera6c687c2018-07-16 15:08:49 -070087
Scott Baker82565472018-08-20 11:40:03 -070088 if (o.s_tag is None):
89 raise Exception("Cannot sync FabricCrossconnectServiceInstance if s_tag is None on fcsi %s" % o.id)
Scott Bakera6c687c2018-07-16 15:08:49 -070090
Scott Baker82565472018-08-20 11:40:03 -070091 if (o.source_port is None):
92 raise Exception("Cannot sync FabricCrossconnectServiceInstance if source_port is None on fcsi %s" % o.id)
93
94 if (not o.switch_datapath_id):
Scott Bakere7b55e42019-04-01 17:18:03 -070095 raise Exception(
96 "Cannot sync FabricCrossconnectServiceInstance if switch_datapath_id is unset on fcsi %s" %
97 o.id)
Scott Baker82565472018-08-20 11:40:03 -070098
Scott Bakere7b55e42019-04-01 17:18:03 -070099 bng_mapping = self.find_bng(s_tag=o.s_tag)
Scott Bakerd443ea72018-08-07 13:50:06 -0700100 if not bng_mapping:
Scott Baker82565472018-08-20 11:40:03 -0700101 raise Exception("Unable to determine BNG port for s_tag %s" % o.s_tag)
Scott Bakerd443ea72018-08-07 13:50:06 -0700102 east_port = bng_mapping.switch_port
Scott Baker547dea02018-07-18 15:24:26 -0700103
Scott Bakere7b55e42019-04-01 17:18:03 -0700104 data = {"deviceId": o.switch_datapath_id,
105 "vlanId": o.s_tag,
Matteo Scandoloc0572042019-08-12 18:45:58 -0700106 "endpoints": [int(o.source_port), int(east_port)]}
Scott Bakera6c687c2018-07-16 15:08:49 -0700107
Scott Baker547dea02018-07-18 15:24:26 -0700108 url = onos['url'] + '/onos/segmentrouting/xconnect'
Scott Bakera6c687c2018-07-16 15:08:49 -0700109
110 self.log.info("Sending request to ONOS", url=url, body=data)
111
112 r = requests.post(url, json=data, auth=HTTPBasicAuth(onos['user'], onos['pass']))
113
114 if r.status_code != 200:
115 raise Exception("Failed to create fabric crossconnect in ONOS: %s" % r.text)
116
Scott Baker82565472018-08-20 11:40:03 -0700117 # TODO(smbaker): If the o.backend_handle changed, then someone must have changed the
118 # FabricCrossconnectServiceInstance. If so, then we potentially need to clean up the old
119 # entry in ONOS. Furthermore, we might want to also save the two port numbers that we used,
120 # to detect someone changing those.
121
122 o.backend_handle = self.make_handle(o.s_tag, o.switch_datapath_id)
Andy Bavierc4f3a982019-02-08 14:55:52 -0700123 o.save_changed_fields()
Scott Baker547dea02018-07-18 15:24:26 -0700124
Scott Bakera6c687c2018-07-16 15:08:49 -0700125 self.log.info("ONOS response", res=r.text)
126
127 def delete_record(self, o):
128 self.log.info("Deleting Fabric Crossconnect Service Instance", service_instance=o)
129
Scott Baker547dea02018-07-18 15:24:26 -0700130 if o.backend_handle:
Himanshu Bhandari32738e82020-06-10 21:08:49 +0530131 onos = Helpers.get_fabric_onos_info(self.model_accessor, o.owner)
Scott Bakera6c687c2018-07-16 15:08:49 -0700132
Scott Baker547dea02018-07-18 15:24:26 -0700133 # backend_handle has everything we need in it to delete this entry.
Scott Baker82565472018-08-20 11:40:03 -0700134 (s_tag, switch_datapath_id) = self.extract_handle(o.backend_handle)
Scott Bakera6c687c2018-07-16 15:08:49 -0700135
Scott Bakere7b55e42019-04-01 17:18:03 -0700136 data = {"deviceId": switch_datapath_id,
137 "vlanId": s_tag}
Scott Bakera6c687c2018-07-16 15:08:49 -0700138
139 url = onos['url'] + '/onos/segmentrouting/xconnect'
140
141 r = requests.delete(url, json=data, auth=HTTPBasicAuth(onos['user'], onos['pass']))
142
143 if r.status_code != 204:
144 raise Exception("Failed to remove fabric crossconnect in ONOS: %s" % r.text)
145
146 self.log.info("ONOS response", res=r.text)