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