blob: 227f3448f27e1d649fd510e8d798ce828e10beb3 [file] [log] [blame]
Matteo Scandolo4e0e88c2017-08-08 13:05:25 -07001
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
Rizwan Haider51fdb3f2016-11-09 18:29:32 -050017import os
18import sys
19
Andrea Campanellaf6e2ed12017-04-03 19:14:25 +020020from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible #if needed
21from synchronizers.new_base.ansible_helper import run_template_ssh #if needed
22from synchronizers.new_base.modelaccessor import *
Rizwan Haider51fdb3f2016-11-09 18:29:32 -050023import requests, json
24from requests.auth import HTTPBasicAuth
25
26from xos.logger import Logger, logging
27
28# vnod local will be in steps/..
29parentdir = os.path.join(os.path.dirname(__file__), "..")
30sys.path.insert(0, parentdir)
31
32logger = Logger(level=logging.INFO)
33
34
35class SyncVnodLocalSystem(SyncStep):
36 provides = [VnodLocalService]
37 observes = VnodLocalService
38 requested_interval = 0
39 initialized = False
40
41 def __init__(self, **args):
42 SyncStep.__init__(self, **args)
43
44 def fetch_pending(self, deletion=False):
45 logger.info("VnodLocal fetch pending called")
46
47 # Some comments to replace as we write the code
48
49 # The AdministrativeState state machine:
50 #
51 # Diasabled (initial)
52 # |
53 # ConfigurationRequested
54 # / / \
55 # / / \
56 # ConfigurationFailed Configured---------DeactivationRequested
57 # \ |
58 # ActivationRequested |
59 # / / \ |
60 # / / \ |
61 # ActivationFailed Enabled -----------
62 #
63 #
64
65 # The OperationalState state machine
66 #
67 # active-----------------|
68 # | |
69 # inactivereported |
70 # | |
71 # inactive----------activereported
72
73 objs = []
74
75
76 # The whole thing needs to be conditional on the VnodLocalSystem existing and being 'enabled'
77 # This is the 'kill switch' in the system that is the first thing to check
78 vnodlocalsystem = self.get_vnodlocal_system()
79
80 if not vnodlocalsystem:
81 logger.debug("No VnodLocal System Configured, skipping sync")
82 return objs
83
84 # Check to make sure the Metro Network System is enabled
85 if vnodlocalsystem.administrativeState == 'disabled':
86 # Nothing to do
87 logger.debug("VnodLocal System configured - state is Disabled, skipping sync")
88 return objs
89
90
91
92 # Handle call when deletion is False
93 if deletion is False:
94
95 # First Part of Auto-attachement: What we need to do is ask the ECORD if there are any Spokes for our site
96 # that are set to 'auto-attached' but are not currently actually attached
97 # it will send back a list of servicehandles that meet that criteria. We will simply
98 # check if we have already created a VnodLocal for that service handle, if we have do
99 # nothing it should be still in progress. If we haven't create it, mark it as 'autoattached', set the
100 # servicehandle and mark it as 'ConfigurationRequested'
101 rest_url = vnodlocalsystem.restUrl
102 sitename = vnodlocalsystem.name
103 username = vnodlocalsystem.username
104 password = vnodlocalsystem.password
105
106 autoattachhandles = self.get_autoattachhandles(vnodlocalsystem)
107 for autoattachhandle in autoattachhandles:
108 # Check to see if it already exists - if not add it
109 if not VnodLocalService.objects.filter(servicehandle=autoattachhandle).exists():
110 vnodlocal = VnodLocalService()
111 vnodlocal.servicehandle = autoattachhandle
112 vnodlocal.autoattached = True
113 vnodlocal.administrativeState = 'configurationrequested'
114 logger.debug("Adding Auto-attached VnodLocalService servicehandle: %s" % vnodlocal.servicehandle)
115 objs.append(vnodlocal)
116
117 # Second Part of Auto-attachment
118 # Look for auto-attachmed Services that are Configured, move them automaticaly to activationrequested
119 autoattachconfigures = self.get_autoattachconfigured()
120 for autoattachconfigure in autoattachconfigures:
121 # Just bounce these forward to activationrequested to get them activated
122 autoattachconfigure.administrativeState = 'activationrequested'
123 objs.append(autoattachconfigure)
124
125
126 # Check for admin status 'ConfigurationRequested'
127 configreqs = VnodLocalService.objects.filter(administrativeState='configurationrequested')
128 for configreq in configreqs:
129 # Call the XOS Interface to configure the service
130 logger.debug("Attempting to configure VnodLocalService servicehandle: %s" % configreq.servicehandle)
131 # Add code to call REST api on the ECORD - For this state - we call VnodGlobal
132 # with the servciehandle and sitename it
133 # it gives us back the NNI port and Vlan Config
134 # we then set our state to 'Configured' or 'ConfigurationFailed'
135 servicehandle = configreq.servicehandle
136 query = {"sitename": sitename, "servicehandle" : servicehandle}
137
138 resp = requests.get("{}/vnodglobal_api_configuration/".format(rest_url), params=query,
139 auth=HTTPBasicAuth(username, password))
140
141 if resp.status_code == 200:
142 resp = resp.json()
143 # Success-path transitions to 'configured'
144 configreq.vlanid = resp['vlanid']
145 configreq.portid = resp['port']['name']
146 configreq.administrativeState = 'configured'
147
148 #update proxy adminstate in ecord
149 data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'configured',
150 "vlanid": configreq.vlanid, "portid": configreq.portid}
151 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
152 auth=HTTPBasicAuth(username, password))
153
154 else:
155 configreq.administrativeState = 'configurationfailed'
156
157 objs.append(configreq)
158
159
160 # Check for admin status 'ActivationRequested'
161 activationreqs = VnodLocalService.objects.filter(administrativeState='activationrequested')
162 for acivationreq in activationreqs:
163 # Call the XOS Interface to activate the service
164 logger.debug("Attempting to activate VnodLocalService servicehandle: %s" % acivationreq.servicehandle)
165 # Add code to call REST api on the ECORD - For this state we send the VnodGlobal
166 # service our service handle, subscriber,
167 # VnodLocalId (this id)
168 # Once this is accepted we transition to the
169 # Final state of 'Enabled' or 'ActivationFailed'
170 servicehandle = acivationreq.servicehandle
171 vnodlocalid = acivationreq.id
172 vlanid = acivationreq.vlanid
173 portid = acivationreq.portid
174
175 data = {"sitename": sitename, "servicehandle": servicehandle, "vnodlocalid": vnodlocalid,
176 "vlanid": vlanid, "portid": portid, "activate": "true"}
177
178 resp = requests.post("{}/vnodglobal_api_activation/".format(rest_url), data=json.dumps(data),
179 auth=HTTPBasicAuth(username, password))
180
181 if resp.status_code == 200:
182 # Success-path transitions to 'enabled'
183 acivationreq.administrativeState = 'enabled'
184
185 # update proxy adminstate in ecord
186 data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'enabled',
187 "vlanid": vlanid, "portid": portid, "operstate": "active"}
188 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
189 auth=HTTPBasicAuth(username, password))
190 else:
191 acivationreq.administrativeState = 'activationfailed'
192
193 # update proxy adminstate in ecord
194 data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'impaired',
195 "operstate": "inactive", "vlanid": vlanid, "portid": portid}
196 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
197 auth=HTTPBasicAuth(username, password))
198
199 objs.append(acivationreq)
200
201
202 # Check for admin status 'DeactivationRequested'
203 deactivationreqs = VnodLocalService.objects.filter(administrativeState='deactivationrequested')
204 for deacivationreq in deactivationreqs:
205 # Call the XOS Interface to de-actiavte the spoke
206 logger.debug("Attempting to de-activate VnodLocalService servicehandle: %s" % deacivationreq.servicehandle)
207 # Add code to call REST api on the ECORD - Report change to VnodGlobal
208 servicehandle = deacivationreq.servicehandle
209 vnodlocalid = deacivationreq.id
210 vlanid = deacivationreq.vlanid
211 portid = deacivationreq.portid
212
213
214 data = {"sitename": sitename, "servicehandle": servicehandle, "vnodlocalid": vnodlocalid,
215 "vlanid": vlanid, "portid": portid, "activate": "false"}
216
217 resp = requests.post("{}/vnodglobal_api_activation/".format(rest_url), data=json.dumps(data),
218 auth=HTTPBasicAuth(username, password))
219
220 if resp.status_code == 200:
221 # Success-path transitions to 'enabled'
222 deacivationreq.administrativeState = 'configured'
223 else:
224 deacivationreq.administrativeState = 'deactivationfailed'
225
226 # update proxy adminstate in ecord
227 data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'impaired',
228 "vlanid": vlanid, "portid": portid}
229 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
230 auth=HTTPBasicAuth(username, password))
231
232 objs.append(deacivationreq)
233
234
235 # Check for oper status inactive reported
236 inactivereports = VnodLocalService.objects.filter(operstate='inactivereported')
237 for inactivereport in inactivereports:
238 # Call the XOS Interface to report operstate issue
239 logger.debug("Attempting to report inactive VnodLocalService servicehandle: %s" % inactivereport.servicehandle)
240 # Add code to call REST api on the ECORD - Report change to VnodGlobal
241
242 servicehandle = inactivereport.servicehandle
243 vlanid = inactivereport.vlanid
244 portid = inactivereport.portid
245
246 # update proxy operstate in ecord
247 data = {"sitename": sitename, "servicehandle": servicehandle, "operstate": "inactive",
248 "adminstate":"impaired", "vlanid": vlanid, "portid": portid}
249 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
250 auth=HTTPBasicAuth(username, password))
251
252 # transition to 'inactive' state regardless of whether call to ECORD was successful?!?
253 inactivereport.operstate = 'inactive'
254 objs.append(inactivereport)
255
256
257 # Check for oper status active reported
258 activereports = VnodLocalService.objects.filter(operstate='activereported')
259 for activereport in activereports:
260 # Call the XOS Interface to report operstate issue
261 logger.debug(
262 "Attempting to report active VnodLocalService servicehandle: %s" % activereport.servicehandle)
263
264 servicehandle = activereport.servicehandle
265 vlanid = activereport.vlanid
266 portid = activereport.portid
267 # Add code to call REST api on the ECORD - Report change to VnodGlobal.
268 # update proxy operstate in ecord
269 data = {"sitename": sitename, "servicehandle": servicehandle, "operstate": "active",
270 "vlanid": vlanid, "portid": portid}
271 resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data),
272 auth=HTTPBasicAuth(username, password))
273
274 activereport.operstate = 'active'
275 objs.append(activereport)
276 elif deletion:
277 # Apply Deletion Semantics:
278 logger.debug("Applying Deletion Semanctics")
279 # TODO: Figure out the odd scenario of Service deletion
280 deletedobjs = VnodLocalService.deleted_objects.all()
281 objs.extend(deletedobjs)
282
283 # Finally just return the set of changed objects
284 return objs
285
286 def get_vnodlocal_system(self):
287 # We only expect to have one of these objects in the system in the curent design
288 # So get the first element from the query
289 vnodlocalsystems = VnodLocalSystem.objects.all()
290 if not vnodlocalsystems:
291 return None
292
293 return vnodlocalsystems[0]
294
295 def get_autoattachhandles(self, vnodlocalsystem):
296 # Figure out API call to actually get this to work
297 rest_url = vnodlocalsystem.restUrl
298 sitename = vnodlocalsystem.name
299 username=vnodlocalsystem.username
300 password=vnodlocalsystem.password
301 query = {"sitename":sitename}
302
303
304 resp = requests.get("{}/vnodglobal_api_autoattach/".format(rest_url), params=query,
305 auth=HTTPBasicAuth(username, password))
306
307 handles = []
308 if resp.status_code == 200:
309 resp = resp.json()
310 handles = resp['servicehandles']
311 else:
312 logger.debug("Request for autoattach servicehandles failed.")
313
314 return handles
315
316 def get_autoattachconfigured(self):
317 # Query for the set of auto-attached handles that are in the 'Configured' state
318 autoattachedconfigured = VnodLocalService.objects.filter(autoattached=True, administrativeState='configured')
319
320 if not autoattachedconfigured:
321 return []
322
323 return autoattachedconfigured
324
325
326 def sync_record(self, o):
327
328 # Simply save the record to the DB - both updates and adds are handled the same way
329 o.save()
330
331
332 def delete_record(self, o):
333 # Overriden to customize our behaviour - the core sync step for will remove the record directly
334 # We just log and return
335 logger.debug("deleting Object %s" % str(o), extra=o.tologdict())
336