def post(self): """ Add a device """ json_data = request.get_json() supported_platforms = ['eos', 'junos', 'ios', 'iosxr', 'nxos', 'nxos_ssh'] data = {} errors = [] data, errors = Device.validate(**json_data) if errors != []: return empty_result(status='error', data=errors), 400 with sqla_session() as session: instance: Device = session.query(Device).filter(Device.hostname == data['hostname']).one_or_none() if instance: errors.append('Device already exists') return empty_result(status='error', data=errors), 400 if 'platform' not in data or data['platform'] not in supported_platforms: errors.append("Device platform not specified or not known (must be any of: {})". format(', '.join(supported_platforms))) return empty_result(status='error', data=errors), 400 if data['device_type'] in ['DIST', 'CORE']: if 'management_ip' not in data or not data['management_ip']: data['management_ip'] = cnaas_nms.confpush.underlay.find_free_mgmt_lo_ip(session) if 'infra_ip' not in data or not data['infra_ip']: data['infra_ip'] = cnaas_nms.confpush.underlay.find_free_infra_ip(session) new_device = Device.device_create(**data) session.add(new_device) session.flush() return empty_result(status='success', data={"added_device": new_device.as_dict()}), 200
def get(self, hostname: str): """ Get device configuration """ result = empty_result() result['data'] = {'config': None} if not Device.valid_hostname(hostname): return empty_result(status='error', data=f"Invalid hostname specified"), 400 try: config, template_vars = cnaas_nms.confpush.sync_devices.generate_only( hostname) result['data']['config'] = { 'hostname': hostname, 'generated_config': config, 'available_variables': template_vars } except Exception as e: logger.exception( f"Exception while generating config for device {hostname}") return empty_result( status='error', data="Exception while generating config for device {}: {} {}". format(hostname, type(e), str(e))), 500 return result
def post(self) -> tuple: """ Download new firmware """ json_data = request.get_json() kwargs = dict() if 'url' not in json_data: return empty_result(status='error', data='Missing parameter url') if 'sha1' not in json_data: return empty_result(status='error', data='Missing parameter sha1') if 'verify_tls' not in json_data: return empty_result(status='error', data='Missing parameter verify_tls') kwargs['url'] = json_data['url'] kwargs['sha1'] = json_data['sha1'] kwargs['verify_tls'] = json_data['verify_tls'] scheduler = Scheduler() job_id = scheduler.add_onetime_job( 'cnaas_nms.api.firmware:get_firmware', when=1, scheduled_by=get_jwt_identity(), kwargs=kwargs) res = empty_result(data='Scheduled job to download firmware') res['job_id'] = job_id return res
def get(self): """ Get settings """ args = request.args hostname = None device_type = None model = None if 'hostname' in args: if Device.valid_hostname(args['hostname']): hostname = args['hostname'] else: return empty_result('error', "Invalid hostname specified"), 400 with sqla_session() as session: dev: Device = session.query(Device).\ filter(Device.hostname == hostname).one_or_none() if dev: device_type = dev.device_type model = dev.model else: return empty_result('error', "Hostname not found in database"), 400 if 'device_type' in args: if DeviceType.has_name(args['device_type'].upper()): device_type = DeviceType[args['device_type'].upper()] else: return empty_result('error', "Invalid device type specified"), 400 try: settings, settings_origin = get_settings(hostname, device_type, model) except Exception as e: return empty_result('error', "Error getting settings: {}".format(str(e))), 400 return empty_result(data={'settings': settings, 'settings_origin': settings_origin})
def get(self, repo): """ Get repository information """ try: repo_type = RepoType[str(repo).upper()] except: return empty_result('error', "Invalid repository type"), 400 return empty_result('success', get_repo_status(repo_type))
def delete(self, device_id): """ Delete device from ID """ json_data = request.get_json() if json_data and 'factory_default' in json_data: if isinstance(json_data['factory_default'], bool) and json_data['factory_default'] is True: scheduler = Scheduler() job_id = scheduler.add_onetime_job( 'cnaas_nms.confpush.erase:device_erase', when=1, scheduled_by=get_jwt_identity(), kwargs={'device_id': device_id}) return empty_result(data='Scheduled job {} to factory default device'.format(job_id)) else: with sqla_session() as session: dev: Device = session.query(Device).filter(Device.id == device_id).one_or_none() if not dev: return empty_result('error', "Device not found"), 404 try: session.delete(dev) session.commit() except IntegrityError as e: session.rollback() return empty_result( status='error', data="Could not remove device because existing references: {}".format(e)) except Exception as e: session.rollback() return empty_result( status='error', data="Could not remove device: {}".format(e)) return empty_result(status="success", data={"deleted_device": dev.as_dict()}), 200
def put(self, hostname): """Bounce selected interfaces by appling bounce-down/bounce-up template""" json_data = request.get_json() if 'bounce_interfaces' in json_data and isinstance(json_data['bounce_interfaces'], list): interfaces: List[str] = json_data['bounce_interfaces'] try: bounce_success = bounce_interfaces(hostname, interfaces) except ValueError as e: return empty_result(status='error', data=str(e)), 400 except Exception as e: return empty_result(status='error', data=str(e)), 500 if bounce_success: return empty_result( status='success', data="Bounced interfaces: {}".format(', '.join(interfaces)) ) else: return empty_result( status='success', data="No error, but no interfaces changed state: {}". format(', '.join(interfaces)) ) else: return empty_result(status='error', data="Unknown action"), 400
def post(self): """ Discover device """ json_data = request.get_json() if 'ztp_mac' not in json_data: return empty_result(status='error', data="POST data must include 'ztp_mac'"), 400 if 'dhcp_ip' not in json_data: return empty_result(status='error', data="POST data must include 'dhcp_ip'"), 400 ztp_mac = json_data['ztp_mac'] dhcp_ip = json_data['dhcp_ip'] job_id = cnaas_nms.confpush.init_device.schedule_discover_device( ztp_mac=ztp_mac, dhcp_ip=dhcp_ip, iteration=1, scheduled_by=get_jwt_identity()) logger.debug( f"Discover device for ztp_mac {ztp_mac} scheduled as ID {job_id}") res = empty_result( data=f"Scheduled job to discover device for ztp_mac {ztp_mac}") res['job_id'] = job_id return res
def get(self, job_id): """ Get job information by ID """ with sqla_session() as session: job = session.query(Job).filter(Job.id == job_id).one_or_none() if job: return empty_result(data={'jobs': [job.as_dict()]}) else: return empty_result(status='error', data="No job with id {} found".format(job_id)), 400
def get(self, id): job = Jobtracker() try: job.load(id) except Exception as e: return empty_result(status='error', data=str(e)), 400 result = empty_result(data={'jobs': [job.to_dict(json_serializable=True)]}) return result
def post(self): json_data = request.get_json() syntax_dict, syntax_dict_origin = merge_dict_origin({}, json_data, {}, 'API POST data') try: ret = check_settings_syntax(syntax_dict, syntax_dict_origin) except SettingsSyntaxError as e: return empty_result(status='error', data=str(e)), 400 else: return empty_result(status='success', data=ret)
def delete(self, device_id): with sqla_session() as session: instance = session.query(Device).filter( Device.id == device_id).one_or_none() if instance: session.delete(instance) session.commit() return empty_result(), 200 else: return empty_result('error', "Device not found"), 404
def post(self, hostname: str): """Restore configuration to previous version""" json_data = request.get_json() apply_kwargs = {'hostname': hostname} config = None if not Device.valid_hostname(hostname): return empty_result(status='error', data=f"Invalid hostname specified"), 400 if 'job_id' in json_data: try: job_id = int(json_data['job_id']) except Exception: return empty_result('error', "job_id must be an integer"), 400 else: return empty_result('error', "job_id must be specified"), 400 with sqla_session() as session: try: prev_config_result = Job.get_previous_config(session, hostname, job_id=job_id) failed = prev_config_result['failed'] if not failed and 'config' in prev_config_result: config = prev_config_result['config'] except JobNotFoundError as e: return empty_result('error', str(e)), 404 except InvalidJobError as e: return empty_result('error', str(e)), 500 except Exception as e: return empty_result('error', "Unhandled exception: {}".format(e)), 500 if failed: return empty_result( 'error', "The specified job_id has a failed status"), 400 if not config: return empty_result('error', "No config found in this job"), 500 if 'dry_run' in json_data and isinstance(json_data['dry_run'], bool) \ and not json_data['dry_run']: apply_kwargs['dry_run'] = False else: apply_kwargs['dry_run'] = True apply_kwargs['config'] = config scheduler = Scheduler() job_id = scheduler.add_onetime_job( 'cnaas_nms.confpush.sync_devices:apply_config', when=1, scheduled_by=get_jwt_identity(), kwargs=apply_kwargs, ) res = empty_result(data=f"Scheduled job to restore {hostname}") res['job_id'] = job_id return res, 200
def post(self): json_data = request.get_json() kwargs: dict = {} if 'hostname' in json_data: hostname = str(json_data['hostname']) if not Device.valid_hostname(hostname): return empty_result( status='error', data=f"Hostname '{hostname}' is not a valid hostname"), 400 with sqla_session() as session: dev: Device = session.query(Device).\ filter(Device.hostname == hostname).one_or_none() if not dev or dev.state != DeviceState.MANAGED: return empty_result( status='error', data= f"Hostname '{hostname}' not found or is not a managed device" ), 400 kwargs['hostname'] = hostname what = hostname elif 'device_type' in json_data: if DeviceType.has_name(str(json_data['device_type']).upper()): kwargs['device_type'] = str(json_data['device_type']).upper() else: return empty_result( status='error', data= f"Invalid device type '{json_data['device_type']}' specified" ), 400 what = f"{json_data['device_type']} devices" elif 'all' in json_data and isinstance(json_data['all'], bool) and json_data['all']: what = "all devices" else: return empty_result( status='error', data=f"No devices to synchronize was specified"), 400 if 'dry_run' in json_data and isinstance(json_data['dry_run'], bool) \ and not json_data['dry_run']: kwargs['dry_run'] = False if 'force' in json_data and isinstance(json_data['force'], bool): kwargs['force'] = json_data['force'] scheduler = Scheduler() job_id = scheduler.add_onetime_job( 'cnaas_nms.confpush.sync_devices:sync_devices', when=1, kwargs=kwargs) res = empty_result(data=f"Scheduled job to synchronize {what}") res['job_id'] = job_id return res
def get(self, device_id): result = empty_result() result['data'] = {'devices': []} with sqla_session() as session: instance = session.query(Device).filter( Device.id == device_id).one_or_none() if instance: result['data']['devices'].append(instance.as_dict()) else: return empty_result('error', "Device not found"), 404 return result
def delete(self, mgmtdomain_id): with sqla_session() as session: instance = session.query(Mgmtdomain).\ filter(Mgmtdomain.id == mgmtdomain_id).one_or_none() if instance: session.delete(instance) session.commit() return empty_result(), 204 else: return empty_result('error', "Management domain not found"), 404
def get(self, hostname): """ Get a device from hostname """ result = empty_result() result['data'] = {'devices': []} with sqla_session() as session: instance = session.query(Device).filter(Device.hostname == hostname).one_or_none() if instance: result['data']['devices'].append(instance.as_dict()) else: return empty_result('error', "Device not found"), 404 return result
def get(self) -> tuple: """ Get firmwares """ try: res = requests.get(get_httpd_url(), verify=verify_tls()) json_data = json.loads(res.content)['data'] except Exception as e: logger.exception(f"Exception when getting images: {e}") return empty_result(status='error', data='Could not get files'), 404 return empty_result(status='success', data=json_data)
def get(self, mgmtdomain_id): result = empty_result() result['data'] = {'mgmtdomains': []} with sqla_session() as session: instance = session.query(Mgmtdomain).\ filter(Mgmtdomain.id == mgmtdomain_id).one_or_none() if instance: result['data']['mgmtdomains'].append(instance.as_dict()) else: return empty_result('error', "Management domain not found"), 404 return result
def put(self): """ Modify plugins """ json_data = request.get_json() if 'action' in json_data: if str(json_data['action']).upper() == 'SELFTEST': pmh = PluginManagerHandler() res = pmh.pm.hook.selftest() return empty_result('success', {'result': res}) else: return empty_result('error', "Unknown action specified"), 400 else: return empty_result('error', "No action specified"), 400
def delete(self): """ Remove job locks """ json_data = request.get_json() if 'name' not in json_data or not json_data['name']: return empty_result('error', "No lock name specified"), 400 with sqla_session() as session: lock = session.query(Joblock).filter(Joblock.name == json_data['name']).one_or_none() if lock: session.delete(lock) else: return empty_result('error', "No such lock found in database"), 404 return empty_result('success', data={'name': json_data['name'], 'status': 'deleted'})
def put(self, device_id): """ Modify device from ID """ json_data = request.get_json() with sqla_session() as session: dev: Device = session.query(Device).filter( Device.id == device_id).one_or_none() if not dev: return empty_result(status='error', data=f"No device with id {device_id}") errors = dev.device_update(**json_data) if errors: return empty_result(status='error', data=errors), 404 return empty_result(status='success', data={"updated_device": dev.as_dict()}), 200
def put(self, repo): """ Modify repository """ json_data = request.get_json() try: repo_type = RepoType[str(repo).upper()] except: return empty_result('error', "Invalid repository type"), 400 if 'action' in json_data: if str(json_data['action']).upper() == 'REFRESH': # TODO: consider doing as scheduled job? try: res = refresh_repo(repo_type, get_jwt_identity()) return empty_result('success', res) except VerifyPathException as e: return empty_result( 'error', "Repository structure is invalid ({}): {}".format( type(e).__name__, str(e))), 400 except JoblockError as e: return empty_result( 'error', "Another job is locking configuration of devices, try again later ({})" .format(str(e))), 503 except SettingsSyntaxError as e: return empty_result( 'error', "Syntax error in repository: {}".format(str(e))), 400 except Exception as e: return empty_result( 'error', "Error in repository: {}".format(str(e))), 500 else: return empty_result('error', "Invalid action"), 400 else: return empty_result('error', "No action specified"), 400
def get(self): """ List all plugins """ try: pmh = PluginManagerHandler() plugindata = pmh.get_plugindata() plugin_module_names = pmh.get_plugins() except Exception as e: return empty_result('error', "Error retrieving plugins {}".format(str(e))) else: return empty_result('success', { 'loaded_plugins': plugin_module_names, 'plugindata': plugindata })
def put(self, hostname): """Take a map of interfaces and associated values to update. Example: {"interfaces": {"Ethernet1": {"configtype": "ACCESS_AUTO"}}} """ json_data = request.get_json() data = {} errors = [] with sqla_session() as session: dev: Device = session.query(Device).filter( Device.hostname == hostname).one_or_none() if not dev: return empty_result('error', "Device not found"), 404 updated = False if 'interfaces' in json_data and isinstance( json_data['interfaces'], dict): for if_name, if_dict in json_data['interfaces'].items(): if not isinstance(if_dict, dict): errors.append( "Each interface must have a dict with data to update" ) continue intf = session.query(Interface).filter(Interface.device == dev).\ filter(Interface.name == if_name).one_or_none() if not intf: errors.append(f"Interface {if_name} not found") continue if 'configtype' in if_dict: configtype = if_dict['configtype'].upper() if InterfaceConfigType.has_name(configtype): intf.configtype = InterfaceConfigType[configtype] updated = True data[if_name] = {'configtype': configtype} else: errors.append( f"Invalid configtype received: {configtype}") if updated: dev.synchronized = False if errors: if data: ret = {'errors': errors, 'updated': data} else: ret = {'errors': errors} return empty_result(status='error', data=ret), 400 else: return empty_result(status='success', data={'updated': data})
def get(self): """ Get all management domains """ result = empty_result() result['data'] = {'mgmtdomains': []} filter_exp = None with sqla_session() as session: query = session.query(Mgmtdomain) try: query = build_filter(Mgmtdomain, query).limit(limit_results()) except Exception as e: return empty_result(status='error', data="Unable to filter mgmtdomains: {}".format(e)), 400 for instance in query: result['data']['mgmtdomains'].append(instance.as_dict()) return result
def post(self): json_data = request.get_json() data = {} errors = [] data, errors = Device.validate(**json_data) if errors != []: return empty_result(status='error', data=errors), 404 with sqla_session() as session: instance: Device = session.query(Device).filter( Device.hostname == data['hostname']).one_or_none() if instance is not None: errors.append('Device already exists') return errors Device.device_add(**json_data) return empty_result(status='success'), 200
def get(self, hostname): result = empty_result() result['data'] = {'interfaces': []} with sqla_session() as session: dev = session.query(Device).filter( Device.hostname == hostname).one_or_none() if not dev: return empty_result('error', "Device not found"), 404 result['data']['hostname'] = dev.hostname intfs = session.query(Interface).filter( Interface.device == dev).all() intf: Interface for intf in intfs: result['data']['interfaces'].append(intf.as_dict()) return result
def put(self, job_id): json_data = request.get_json() if 'action' not in json_data: return empty_result(status='error', data="Action must be specified"), 400 with sqla_session() as session: job = session.query(Job).filter(Job.id == job_id).one_or_none() if not job: return empty_result( status='error', data="No job with id {} found".format(job_id)), 400 job_status = job.status action = str(json_data['action']).upper() if action == 'ABORT': allowed_jobstates = [JobStatus.SCHEDULED, JobStatus.RUNNING] if job_status not in allowed_jobstates: return empty_result( status='error', data="Job id {} is in state {}, must be {} to abort". format(job_id, job_status, (" or ".join([x.name for x in allowed_jobstates])))), 400 abort_reason = "Aborted via API call" if 'abort_reason' in json_data and isinstance( json_data['abort_reason'], str): abort_reason = json_data['abort_reason'][:255] abort_reason += " (aborted by {})".format(get_jwt_identity()) if job_status == JobStatus.SCHEDULED: scheduler = Scheduler() scheduler.remove_scheduled_job(job_id=job_id, abort_message=abort_reason) time.sleep(2) elif job_status == JobStatus.RUNNING: with sqla_session() as session: job = session.query(Job).filter( Job.id == job_id).one_or_none() job.status = JobStatus.ABORTING with sqla_session() as session: job = session.query(Job).filter(Job.id == job_id).one_or_none() return empty_result(data={"jobs": [job.as_dict()]}) else: return empty_result(status='error', data="Unknown action: {}".format(action)), 400
def get(self): result = {'linknet': []} with sqla_session() as session: query = session.query(Linknet) for instance in query: result['linknet'].append(instance.as_dict()) return empty_result(status='success', data=result)