| import os |
| import sys |
| |
| from synchronizers.base.syncstep import SyncStep |
| from services.vnodlocal.models import * |
| import requests, json |
| from requests.auth import HTTPBasicAuth |
| |
| from xos.logger import Logger, logging |
| |
| # vnod local will be in steps/.. |
| parentdir = os.path.join(os.path.dirname(__file__), "..") |
| sys.path.insert(0, parentdir) |
| |
| logger = Logger(level=logging.INFO) |
| |
| |
| class SyncVnodLocalSystem(SyncStep): |
| provides = [VnodLocalService] |
| observes = VnodLocalService |
| requested_interval = 0 |
| initialized = False |
| |
| def __init__(self, **args): |
| SyncStep.__init__(self, **args) |
| |
| def fetch_pending(self, deletion=False): |
| logger.info("VnodLocal fetch pending called") |
| |
| # Some comments to replace as we write the code |
| |
| # The AdministrativeState state machine: |
| # |
| # Diasabled (initial) |
| # | |
| # ConfigurationRequested |
| # / / \ |
| # / / \ |
| # ConfigurationFailed Configured---------DeactivationRequested |
| # \ | |
| # ActivationRequested | |
| # / / \ | |
| # / / \ | |
| # ActivationFailed Enabled ----------- |
| # |
| # |
| |
| # The OperationalState state machine |
| # |
| # active-----------------| |
| # | | |
| # inactivereported | |
| # | | |
| # inactive----------activereported |
| |
| objs = [] |
| |
| |
| # The whole thing needs to be conditional on the VnodLocalSystem existing and being 'enabled' |
| # This is the 'kill switch' in the system that is the first thing to check |
| vnodlocalsystem = self.get_vnodlocal_system() |
| |
| if not vnodlocalsystem: |
| logger.debug("No VnodLocal System Configured, skipping sync") |
| return objs |
| |
| # Check to make sure the Metro Network System is enabled |
| if vnodlocalsystem.administrativeState == 'disabled': |
| # Nothing to do |
| logger.debug("VnodLocal System configured - state is Disabled, skipping sync") |
| return objs |
| |
| |
| |
| # Handle call when deletion is False |
| if deletion is False: |
| |
| # First Part of Auto-attachement: What we need to do is ask the ECORD if there are any Spokes for our site |
| # that are set to 'auto-attached' but are not currently actually attached |
| # it will send back a list of servicehandles that meet that criteria. We will simply |
| # check if we have already created a VnodLocal for that service handle, if we have do |
| # nothing it should be still in progress. If we haven't create it, mark it as 'autoattached', set the |
| # servicehandle and mark it as 'ConfigurationRequested' |
| rest_url = vnodlocalsystem.restUrl |
| sitename = vnodlocalsystem.name |
| username = vnodlocalsystem.username |
| password = vnodlocalsystem.password |
| |
| autoattachhandles = self.get_autoattachhandles(vnodlocalsystem) |
| for autoattachhandle in autoattachhandles: |
| # Check to see if it already exists - if not add it |
| if not VnodLocalService.objects.filter(servicehandle=autoattachhandle).exists(): |
| vnodlocal = VnodLocalService() |
| vnodlocal.servicehandle = autoattachhandle |
| vnodlocal.autoattached = True |
| vnodlocal.administrativeState = 'configurationrequested' |
| logger.debug("Adding Auto-attached VnodLocalService servicehandle: %s" % vnodlocal.servicehandle) |
| objs.append(vnodlocal) |
| |
| # Second Part of Auto-attachment |
| # Look for auto-attachmed Services that are Configured, move them automaticaly to activationrequested |
| autoattachconfigures = self.get_autoattachconfigured() |
| for autoattachconfigure in autoattachconfigures: |
| # Just bounce these forward to activationrequested to get them activated |
| autoattachconfigure.administrativeState = 'activationrequested' |
| objs.append(autoattachconfigure) |
| |
| |
| # Check for admin status 'ConfigurationRequested' |
| configreqs = VnodLocalService.objects.filter(administrativeState='configurationrequested') |
| for configreq in configreqs: |
| # Call the XOS Interface to configure the service |
| logger.debug("Attempting to configure VnodLocalService servicehandle: %s" % configreq.servicehandle) |
| # Add code to call REST api on the ECORD - For this state - we call VnodGlobal |
| # with the servciehandle and sitename it |
| # it gives us back the NNI port and Vlan Config |
| # we then set our state to 'Configured' or 'ConfigurationFailed' |
| servicehandle = configreq.servicehandle |
| query = {"sitename": sitename, "servicehandle" : servicehandle} |
| |
| resp = requests.get("{}/vnodglobal_api_configuration/".format(rest_url), params=query, |
| auth=HTTPBasicAuth(username, password)) |
| |
| if resp.status_code == 200: |
| resp = resp.json() |
| # Success-path transitions to 'configured' |
| configreq.vlanid = resp['vlanid'] |
| configreq.portid = resp['port']['name'] |
| configreq.administrativeState = 'configured' |
| |
| #update proxy adminstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'configured', |
| "vlanid": configreq.vlanid, "portid": configreq.portid} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| else: |
| configreq.administrativeState = 'configurationfailed' |
| |
| objs.append(configreq) |
| |
| |
| # Check for admin status 'ActivationRequested' |
| activationreqs = VnodLocalService.objects.filter(administrativeState='activationrequested') |
| for acivationreq in activationreqs: |
| # Call the XOS Interface to activate the service |
| logger.debug("Attempting to activate VnodLocalService servicehandle: %s" % acivationreq.servicehandle) |
| # Add code to call REST api on the ECORD - For this state we send the VnodGlobal |
| # service our service handle, subscriber, |
| # VnodLocalId (this id) |
| # Once this is accepted we transition to the |
| # Final state of 'Enabled' or 'ActivationFailed' |
| servicehandle = acivationreq.servicehandle |
| vnodlocalid = acivationreq.id |
| vlanid = acivationreq.vlanid |
| portid = acivationreq.portid |
| |
| data = {"sitename": sitename, "servicehandle": servicehandle, "vnodlocalid": vnodlocalid, |
| "vlanid": vlanid, "portid": portid, "activate": "true"} |
| |
| resp = requests.post("{}/vnodglobal_api_activation/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| if resp.status_code == 200: |
| # Success-path transitions to 'enabled' |
| acivationreq.administrativeState = 'enabled' |
| |
| # update proxy adminstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'enabled', |
| "vlanid": vlanid, "portid": portid, "operstate": "active"} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| else: |
| acivationreq.administrativeState = 'activationfailed' |
| |
| # update proxy adminstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'impaired', |
| "operstate": "inactive", "vlanid": vlanid, "portid": portid} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| objs.append(acivationreq) |
| |
| |
| # Check for admin status 'DeactivationRequested' |
| deactivationreqs = VnodLocalService.objects.filter(administrativeState='deactivationrequested') |
| for deacivationreq in deactivationreqs: |
| # Call the XOS Interface to de-actiavte the spoke |
| logger.debug("Attempting to de-activate VnodLocalService servicehandle: %s" % deacivationreq.servicehandle) |
| # Add code to call REST api on the ECORD - Report change to VnodGlobal |
| servicehandle = deacivationreq.servicehandle |
| vnodlocalid = deacivationreq.id |
| vlanid = deacivationreq.vlanid |
| portid = deacivationreq.portid |
| |
| |
| data = {"sitename": sitename, "servicehandle": servicehandle, "vnodlocalid": vnodlocalid, |
| "vlanid": vlanid, "portid": portid, "activate": "false"} |
| |
| resp = requests.post("{}/vnodglobal_api_activation/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| if resp.status_code == 200: |
| # Success-path transitions to 'enabled' |
| deacivationreq.administrativeState = 'configured' |
| else: |
| deacivationreq.administrativeState = 'deactivationfailed' |
| |
| # update proxy adminstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "adminstate": 'impaired', |
| "vlanid": vlanid, "portid": portid} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| objs.append(deacivationreq) |
| |
| |
| # Check for oper status inactive reported |
| inactivereports = VnodLocalService.objects.filter(operstate='inactivereported') |
| for inactivereport in inactivereports: |
| # Call the XOS Interface to report operstate issue |
| logger.debug("Attempting to report inactive VnodLocalService servicehandle: %s" % inactivereport.servicehandle) |
| # Add code to call REST api on the ECORD - Report change to VnodGlobal |
| |
| servicehandle = inactivereport.servicehandle |
| vlanid = inactivereport.vlanid |
| portid = inactivereport.portid |
| |
| # update proxy operstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "operstate": "inactive", |
| "adminstate":"impaired", "vlanid": vlanid, "portid": portid} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| # transition to 'inactive' state regardless of whether call to ECORD was successful?!? |
| inactivereport.operstate = 'inactive' |
| objs.append(inactivereport) |
| |
| |
| # Check for oper status active reported |
| activereports = VnodLocalService.objects.filter(operstate='activereported') |
| for activereport in activereports: |
| # Call the XOS Interface to report operstate issue |
| logger.debug( |
| "Attempting to report active VnodLocalService servicehandle: %s" % activereport.servicehandle) |
| |
| servicehandle = activereport.servicehandle |
| vlanid = activereport.vlanid |
| portid = activereport.portid |
| # Add code to call REST api on the ECORD - Report change to VnodGlobal. |
| # update proxy operstate in ecord |
| data = {"sitename": sitename, "servicehandle": servicehandle, "operstate": "active", |
| "vlanid": vlanid, "portid": portid} |
| resp = requests.post("{}/vnodglobal_api_status/".format(rest_url), data=json.dumps(data), |
| auth=HTTPBasicAuth(username, password)) |
| |
| activereport.operstate = 'active' |
| objs.append(activereport) |
| elif deletion: |
| # Apply Deletion Semantics: |
| logger.debug("Applying Deletion Semanctics") |
| # TODO: Figure out the odd scenario of Service deletion |
| deletedobjs = VnodLocalService.deleted_objects.all() |
| objs.extend(deletedobjs) |
| |
| # Finally just return the set of changed objects |
| return objs |
| |
| def get_vnodlocal_system(self): |
| # We only expect to have one of these objects in the system in the curent design |
| # So get the first element from the query |
| vnodlocalsystems = VnodLocalSystem.objects.all() |
| if not vnodlocalsystems: |
| return None |
| |
| return vnodlocalsystems[0] |
| |
| def get_autoattachhandles(self, vnodlocalsystem): |
| # Figure out API call to actually get this to work |
| rest_url = vnodlocalsystem.restUrl |
| sitename = vnodlocalsystem.name |
| username=vnodlocalsystem.username |
| password=vnodlocalsystem.password |
| query = {"sitename":sitename} |
| |
| |
| resp = requests.get("{}/vnodglobal_api_autoattach/".format(rest_url), params=query, |
| auth=HTTPBasicAuth(username, password)) |
| |
| handles = [] |
| if resp.status_code == 200: |
| resp = resp.json() |
| handles = resp['servicehandles'] |
| else: |
| logger.debug("Request for autoattach servicehandles failed.") |
| |
| return handles |
| |
| def get_autoattachconfigured(self): |
| # Query for the set of auto-attached handles that are in the 'Configured' state |
| autoattachedconfigured = VnodLocalService.objects.filter(autoattached=True, administrativeState='configured') |
| |
| if not autoattachedconfigured: |
| return [] |
| |
| return autoattachedconfigured |
| |
| |
| def sync_record(self, o): |
| |
| # Simply save the record to the DB - both updates and adds are handled the same way |
| o.save() |
| |
| |
| def delete_record(self, o): |
| # Overriden to customize our behaviour - the core sync step for will remove the record directly |
| # We just log and return |
| logger.debug("deleting Object %s" % str(o), extra=o.tologdict()) |
| |