def delete_all(api, workflow_dict): """ Deletes all Wireless Design Information regardless of input from workflow_dict. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: Not used; :returns: Nothing """ _schema = None logger.info('wireless::delete all') logger.debug('schema: {}'.format(_schema)) # SSID's to remove ssids = api.non_fabric_wireless.get_enterprise_ssid() # delete SSIDs if ssids: for ssid in ssids: for detail in ssid['ssidDetails']: logger.info('Delete Wireless::delete SSID {}'.format(detail['name'])) result = api.non_fabric_wireless.delete_enterprise_ssid(detail['name']) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.info('Delete Wireless: No SSIDs to delete') # delete profiles try: profiles = api.non_fabric_wireless.get_wireless_profile() if profiles: for profile in profiles: no_site_profile = {'profileName': profile['profileDetails']['name'], 'sites': ""} # remove sites from profile data = common.build_json_from_template(templates.wireless_profile_j2, no_site_profile) result = api.non_fabric_wireless.update_wireless_profile(payload=data) logger.debug(result) common.wait_for_task_completion(api, result) # now good to delete profile logger.info('Delete Wireless::delete Profile {}'.format(profile['profileDetails']['name'])) wireless_profile_delete_url = "/dna/intent/api/v1/wireless-profile/{}".format( profile['profileDetails']['name']) result = api.custom_caller.call_api('DELETE', wireless_profile_delete_url) logger.debug(result) common.wait_for_task_completion(api, result) except: logger.info('delete_all::no wireless profiles found') # delete wireless interfaces. Can't delete management so we have to make sure it is added interface = json.dumps([{"interfaceName": "management", "vlanId": 0}]) data = common.build_json_from_template(templates.wireless_interface_j2, interface) result = api.custom_caller.call_api('POST', wireless_int_url, json=data) logger.debug(result) common.wait_for_task_completion(api, result['response'])
def create_network_settings(api, workflow_dict): """ Creates DNA Center Global Network Settings based on input from workflow_dict. If adding Client/Network AAA server settings, AAA server will need to already be configured in DNA Center. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows Network Settings (DHCP, DNS, etc) (see schema.yaml); :returns: Nothing """ _schema = 'globalSettings.schema.network_settings' logger.info('network_settings::create_network_settings') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # get settings for config settings = {} # rearrange into 1 dict so it is easier to work with for row in table_data: settings[row['item']] = row['value'] csv_fields = ['dhcpServer', 'syslogServer', 'snmpServer', 'ntpServer'] # convert csv strings to List for field in csv_fields: if settings[field]: settings[field] = common.csv_string_to_list(settings[field]) if settings: # proceed if site specified - if not exit if settings['site']: site_info = api.sites.get_site(name=settings['site']) site_id = site_info['response'][0]['id'] if site_info[ 'response'] else None # build URI and create network settings create_network_settings_uri = "{}/{}".format( network_settings_intent_uri, site_id) # using json builder from helper file data = common.build_json_from_template( templates.network_settings_intent_j2, settings) result = api.custom_caller.call_api( 'POST', create_network_settings_uri, json=data) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.error( 'network_settings::create_network_settings:: Site required to update network settings' ) pass else: logger.error('schema not found: {}'.format(_schema))
def create_wireless_profile(api, workflow_dict): """ Creates DNA Center Wireless Profiles. Use for creation of new wireless profiles. If updating profile see "update_wireless_profile" :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of Wireless Profiles with associated parameters. (see schema.yaml); :returns: Nothing """ _schema = 'wireless_profiles.schema.wireless' logger.info('wireless::create_wireless_profile') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # Intent api broken when GET for wireless profile and no current values # _ent_profile_db = api.non_fabric_wireless.get_wireless_profile() # Work around Hack result = api.custom_caller.call_api('GET', temp_get_wireless_profile_url) _ent_profile_db = result['response'] # Get current profile names if _ent_profile_db: profile_list = [profile['name'] for profile in _ent_profile_db] else: profile_list = [] for row in table_data: if 'present' in row['presence']: # If sites comma separated, split them up into a list if "," in row['sites']: row['sites'] = row['sites'].split(",") row['sites'] = [item.lstrip() for item in row['sites']] else: row['sites'] = [row['sites']] if row['profileName'] in profile_list: logger.info('Profile: {} already exists'.format(row['profileName'])) pass else: logger.info('Creating Profile: {}'.format(row['profileName'])) data = common.build_json_from_template(templates.wireless_profile_j2, row) result = api.non_fabric_wireless.create_wireless_profile(payload=data) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.error('schema not found: {}'.format(_schema))
def update_wireless_profile(api, workflow_dict): """ Updates DNA Center Wireless Profiles. Use for updating of existing wireless profiles. If creating profile see "create_wireless_profile" :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of Wireless Profiles with associated parameters. (see schema.yaml); :returns: Nothing """ _schema = 'wireless_profiles.schema.wireless' logger.info('wireless::update_wireless_profile') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # API does not seem to like setting SSID info during create process so have to use update to add SSID details for row in table_data: if 'present' in row['presence']: # Get current profile names _ent_profile_db = api.non_fabric_wireless.get_wireless_profile() # Get Current Profiles profile_list = [profile['profileDetails']['name'] for profile in _ent_profile_db] # If sites comma separated, split them up into a list if "," in row['sites']: row['sites'] = row['sites'].split(",") row['sites'] = [item.lstrip() for item in row['sites']] else: row['sites'] = [row['sites']] # filter out mapping for specific profile in row filtered_mappings = [mappings for mappings in workflow_dict['ssid_to_profile_mapping.schema.wireless'] if mappings['profileName'] == row['profileName'] and mappings['presence'] == "present"] row['ssidDetails'] = filtered_mappings if row['profileName'] in profile_list: logger.info('Updating Profile: {}'.format(row['profileName'])) data = common.build_json_from_template(templates.wireless_profile_j2, row) result = api.non_fabric_wireless.update_wireless_profile(payload=data) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.info('Profile: {} does not exist'.format(row['profileName'])) pass else: logger.error('schema not found: {}'.format(_schema))
def provision_devices(api, workflow_dict): _schema = 'devices.schema.devices' logger.info('devices::provision_devices') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] sites_db = api.sites.get_site() devices_db = api.devices.get_device_list() for device in table_data: if device['presence'] == 'absent' or device[ 'provisionDevice'] == False: continue site_id = common.get_object_id( sites_db['response'], siteNameHierarchy=device['location']) device_id = common.get_object_id(devices_db['response'], hostname=device['hostname'], strict=False) device_name = common.get_object_id(devices_db['response'], return_param='hostname', hostname=device['hostname'], strict=False) logger.info('Provisioning for device: {}, at site: {}'.format( device['hostname'], device['location'])) payload_data = {} payload_data.update({'name': device_name}) payload_data.update({'networkDeviceId': device_id}) payload_data.update({'siteId': site_id}) data = common.build_json_from_template( templates.provision_device_j2, payload_data) result = api.custom_caller.call_api('POST', provision_device_url, json=data) status = common.wait_for_task_completion(api, result['response'], timeout=45, tree=True) logger.debug(status) else: logger.error('schema not found: {}'.format(_schema))
def provision_wireless_device(api, workflow_dict): """ Provisions Wireless LAN Controllers (WLCs) in Cisco DNA Center. Flexconnect and OTT interface parameters (if in use) are required input or provision will fail :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of Wireless WLCs with associated parameters. (see schema.yaml); :returns: Nothing """ _schema = 'wireless_provisioning.schema.wireless' logger.info('wireless::provision_wireless_device') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # get devices to be provisioned. new_provision_devices = [device for device in table_data if device['presence'] == 'present'] if new_provision_devices: for device in new_provision_devices: # break up managed locations if csv if "," in device['managedAPLocations']: device['managedAPLocations'] = device['managedAPLocations'].split(",") else: device['managedAPLocations'] = [device['managedAPLocations']] # #get IP interface configs for specific WLC # Needed for centrail SSID's but not yet implemented wireless_int_detail = [detail for detail in workflow_dict['wireless_profisioning_interface.schema.wireless'] if detail['deviceName'] == device['deviceName'] and detail['presence'] == 'present'] if wireless_int_detail: device['dynamicInterfaces'] = wireless_int_detail logger.info('Provisioning WLC: {}'.format(device['deviceName'])) data = common.build_json_from_template(templates.provision_j2, device) result = api.non_fabric_wireless.provision(payload=data) logger.debug(result) common.wait_for_task_completion(api, result, timeout=30) else: logger.info('Provisioning WLC: No WLCs to Provision') pass else: logger.error('schema not found: {}'.format(_schema))
def _create_floors(api, sites_db, table_data): _table_key = 'name' # Cycle through the rows and create entries with 'present' set for row in table_data: if 'present' in row['presence'] and 'floor' in row['type']: site_name_hierarchy = '{}/{}'.format(row['parentName'], row['name']) _id = common.get_object_id(sites_db, siteNameHierarchy=site_name_hierarchy) if _id is not None: logger.info('Floor: {}/{} already exists with id: {}'.format(row['parentName'], row[_table_key], _id)) else: logger.info('Creating floor: {}/{}'.format(row['parentName'], row[_table_key])) data = common.build_json_from_template(templates.floor_j2, row) logger.debug('Building payload: {}'.format(data)) result = api.sites.create_site(payload=data) status = common.wait_for_task_completion(api, result) logger.debug(status)
def add_device_role(api, workflow_dict): _schema = 'devices.schema.devices' logger.info('devices::delete_devices') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] device_role_changes = [row for row in table_data if row['role']] _device_db = api.devices.get_device_list()['response'] device_name_list = [(device['hostname'], device['id']) for device in _device_db] device_uuid_list = [] # get device id for names without domain name attached if device_role_changes: for device in device_name_list: for change_device in device_role_changes: if device[0].split( '.')[0] == change_device['hostname'].split('.')[0]: device_uuid_list.append({ 'hostname': change_device['hostname'], 'id': device[1], 'role': change_device['role'] }) # now push the role changes if device_uuid_list: for device in device_uuid_list: data = common.build_json_from_template( templates.device_role_j2, device) result = api.devices.update_device_role(payload=data) status = common.wait_for_task_completion( api, result['response']) logger.debug(status) else: logger.error("Device does not exist. Role not updated") else: logger.error('schema not found: {}'.format(_schema))
def add_aaa(api, workflow_dict): """ Creates DNA Center Global AAA Server based on input from workflow_dict. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows with AAA server settings required (see schema.yaml); :returns: Nothing """ _schema = 'aaa.schema.system_settings' logger.info('system_settings::add_aaa') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # get Current AAA Servers and get a name list _aaa_db = api.custom_caller.call_api('GET', aaa_uri)['response'] if _aaa_db: aaa_ips = [aaa['ipAddress'] for aaa in _aaa_db] else: aaa_ips = [] # process each row for row in table_data: # if present marked and ise not already added to dnac if row['presence'] == 'present': if row['ipAddress'] not in aaa_ips: logger.info('Adding AAA: {}. This may take some time...'.format(row['ipAddress'])) data = common.build_json_from_template(templates.aaa_j2, row) result = api.custom_caller.call_api('POST', aaa_uri, json=data) status = common.wait_for_task_completion(api, result.response, timeout=60) logger.debug(status) # if added successfully, wait until ise process is completed if not status['response']['isError']: logger.info('Waiting on AAA to move from INPROGRESS to ACTIVE') _wait_on_ise_completion(api, row) else: logger.error(status['response']['failureReason']) else: logger.error('schema not found: {}'.format(_schema))
def create_enterprise_ssid(api, workflow_dict): """ Creates DNA Center Enterprise SSIDs. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of Enterprise SSID definitions (see schema.yaml); :returns: Nothing """ _schema = 'ssids.schema.wireless' logger.info('wireless::create_enterprise_ssid') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] _ent_ssid_db = api.non_fabric_wireless.get_enterprise_ssid() # Get existing SSID List ssid_list = [] for item in _ent_ssid_db,: if item: for wlan_profile in item: for ssid in wlan_profile['ssidDetails']: ssid_list.append(ssid['name']) else: ssid_list = [] for row in table_data: if 'present' in row['presence']: if row['name'] in ssid_list: logger.info('SSID: {} already exists'.format(row['name'])) pass else: logger.info('Creating Enterprise SSID: {}'.format(row['name'])) data = common.build_json_from_template(templates.ssid_j2, row) result = api.non_fabric_wireless.create_enterprise_ssid(payload=data) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.error('schema not found: {}'.format(_schema))
def deploy_templates(api, workflow_dict): """ Deploys Templates :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of devices and templates to be pushed (see schema.py); :returns: Nothing """ _schema = 'deploy.schema.templates' logger.info('templates::deploy_templates') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] deployment_details = [ device for device in table_data if device['presence'] == 'present' ] for deployment in deployment_details: # get template id response = api.template_programmer.gets_the_templates_available() template_data = common.get_object_by_attribute( response, name=deployment['templateName']) # if template exists go ahead and deploy if template_data['templateId'] is not None: deployment['templateId'] = template_data['templateId'] data = common.build_json_from_template( json_templates.deploy_template_j2, deployment) response = api.template_programmer.deploy_template( payload=data) # api response is broken for deployment api response. just printing the response for now # common.wait_for_task_completion(response) return response else: logger.error('schema not found: {}'.format(_schema))
def _create_buildings(api, sites_db, table_data): _table_key = 'name' # Cycle through the rows and create entries with 'present' set for row in table_data: if 'present' in row['presence'] and 'building' in row['type']: site_name_hierarchy = '{}/{}'.format(row['parentName'], row['name']) _id = common.get_object_id(sites_db, siteNameHierarchy=site_name_hierarchy) if _id is not None: logger.info('Building: {}/{} already exists with id: {}'.format(row['parentName'], row[_table_key], _id)) else: logger.info('Creating building: {}/{}'.format(row['parentName'], row[_table_key])) data = common.build_json_from_template(templates.building_j2, row) data['site']['building']['address'] = '{}, {}, {}'.format( row['street'], row['city'], row['country'] ) # If lat and lon are defined, they are used instead of _address_lookup if row['latitude'] is not None and row['longitude'] is not None: data['site']['building']['longitude'] = float(row['longitude']) data['site']['building']['latitude'] = float(row['latitude']) else: location = _address_lookup(row['street'], row['city'], row['country']) if location is not None: logger.info('Address lookup: SUCCESS') data['site']['building']['address'] = location['address'] data['site']['building']['longitude'] = float(location['lon']) data['site']['building']['latitude'] = float(location['lat']) else: logger.info('Address lookup: FAILURE') logger.debug('Building payload: {}'.format(data)) result = api.sites.create_site(payload=data) status = common.wait_for_task_completion(api, result) logger.debug(status)
def delete_wireless(api, workflow_dict): """ Deletes wireless SSID's, profiles, and interfaces specified in workflow_dict. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of wireless information (see schema.yaml); :returns: Nothing """ _schema = 'ssids.schema.wireless' logger.info('wireless::delete_wireless::ssids') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # SSID's to remove ssids = [ssid['name'] for ssid in table_data if ssid['presence'] == "absent"] # delete new SSIDs if ssids: for ssid in ssids: logger.info('Delete Wireless::delete SSID {}'.format(ssid)) result = api.non_fabric_wireless.delete_enterprise_ssid(ssid) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.info('Delete Wireless: No SSIDs to delete') else: logger.error('schema not found: {}'.format(_schema)) _schema = 'wireless_profiles.schema.wireless' logger.info('wireless::delete_wireless::wireless_profiles') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] profiles = [profile['profileName'] for profile in table_data if profile['presence'] == "absent"] # remove wireless Profiles if profiles: for profile in profiles: no_site_profile = {'profileName': profile, 'sites': ""} # remove sites from profile data = common.build_json_from_template(templates.wireless_profile_j2, no_site_profile) result = api.non_fabric_wireless.update_wireless_profile(payload=data) logger.debug(result) common.wait_for_task_completion(api, result) # now good to delete profile logger.info('Delete Wireless::delete Profile {}'.format(profile)) wireless_profile_delete_url = "/dna/intent/api/v1/wireless-profile/{}".format(profile) result = api.custom_caller.call_api('DELETE', wireless_profile_delete_url) logger.debug(result) common.wait_for_task_completion(api, result) else: logger.info('Delete Wireless: No Profiles to delete') else: logger.error('schema not found: {}'.format(_schema)) _schema = 'ssid_to_profile_mapping.schema.wireless' logger.info('wireless::delete_wireless::ssid_to_profile_mapping') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # delete wireless interfaces # Get current interface list result = api.custom_caller.call_api('GET', wireless_int_url) current_wireless_interfaces = result['response'][0]['value'] # Get interfaces to remove interfaces_to_remove = [interface['interfaceNameOTT'] for interface in table_data if interface['interfaceNameOTT'] is not None and interface['presence'] == "absent"] # create remove targeted interfaces from list and post new list if interfaces_to_remove: interfaces = json.dumps([interface for interface in current_wireless_interfaces if interface['interfaceName'] not in interfaces_to_remove]) logger.info('Deleting Wireless Interfaces ') data = common.build_json_from_template(templates.wireless_interface_j2, interfaces) result = api.custom_caller.call_api('POST', wireless_int_url, json=data) logger.debug(result) common.wait_for_task_completion(api, result['response']) else: logger.info('Delete Wireless: No Wireless Interfaces to delete') else: logger.error('schema not found: {}'.format(_schema))
def run_discovery(api, workflow_dict): """ Initiates DNA Center Device Discovery based on input from workflow_dict. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of discovery job definitions (see schema.yaml); :returns: Nothing """ _schema = 'discovery.schema.discovery' logger.info('discovery::run_discovery') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] get_discoveries_url = '{}/1/99'.format(discovery_url) # Cycle through the rows and create entries with 'present' set for row in table_data: if row['presence'] and 'present' in row['presence']: # URI: GET to discover ID of existing discoveries so that we can delete/update _discovery = api.custom_caller.call_api('GET', get_discoveries_url) _id = common.get_object_id(_discovery['response'], name=row['name']) if _id is not None: data = { 'id': _id, 'discoveryStatus': 'Active'} # URI: PUT to start/rerun an existing discovery # discovery_url = 'api/v1/discovery' result = api.custom_caller.call_api('PUT', discovery_url, json=data) logger.info('Waiting a few seconds for discovery to start ...') time.sleep(5) status = common.wait_for_task_completion(api, result['response'], timeout=30) logger.debug(status) _wait_for_discovery_to_complete(api, _id) else: _creds = [] _cli = api.network_discovery.get_global_credentials('CLI') _id = common.get_object_id(_cli['response'], description=row['cli']) _creds.append(_id) _cli = api.network_discovery.get_global_credentials('SNMPV2_READ_COMMUNITY') _id = common.get_object_id(_cli['response'], description=row['snmp_ro']) _creds.append(_id) _cli = api.network_discovery.get_global_credentials('SNMPV2_WRITE_COMMUNITY') _id = common.get_object_id(_cli['response'], description=row['snmp_rw']) _creds.append(_id) _discovery_range = '{}-{}'.format(row['startIp'], row['endIp']) row.update({'discovery_range': _discovery_range}) data = common.build_json_from_template(templates.discovery_j2, row) data['globalCredentialIdList'] = _creds logger.info('Adding discovery ... ') result = api.network_discovery.start_discovery(payload=data) status = common.wait_for_task_completion(api, result['response']) logger.debug(status) _discovery = api.custom_caller.call_api('GET', get_discoveries_url) _id = common.get_object_id(_discovery['response'], name=row['name']) _wait_for_discovery_to_complete(api, _id) else: logger.error('schema not found: {}'.format(_schema))
def assign_to_site(api, workflow_dict): _schema = 'devices.schema.devices' logger.info('devices::assign_to_site') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # Hack until we figure out how to check device inventory status correctly # logger.info('devices::Sleep for 5 minutes ...') # time.sleep(300) sites_db = api.sites.get_site() for device in table_data: if 'absent' == device['presence']: continue site_id = common.get_object_id( sites_db['response'], siteNameHierarchy=device['location']) topology_db = api.custom_caller.call_api('GET', topology_db_uri) device_ip = common.get_object_id(topology_db['response']['nodes'], return_param='ip', label=device['hostname'], strict=False) if device_ip is None: logger.info('Device {} not found in topology database'.format( device['hostname'])) logger.info('Entering wait loop for maximum 60 seconds') topology_db = _wait_for_device_presence(api, device['hostname'], timeout=60) device_ip = common.get_object_id( topology_db['response']['nodes'], return_param='ip', label=device['hostname'], strict=False) if site_id is not None: # TODO Can this be converted to native SDK call? # URI: GET used to discover discover site membership site_member_uri = site_member_uri_pattern.format(site_id) site_membership = api.custom_caller.call_api( 'GET', site_member_uri) device_provisioned = 0 for members_response in site_membership['device']: # Check if device already provisioned under this site # This is suboptimal since the device could be provisioned under a different site already if len(members_response['response']): device_id = common.get_object_id( members_response['response'], return_param='instanceUuid', hostname=device['hostname'], strict=False) if device_id is not None: logger.info( 'Device {} already allocated to site {}'. format(device['hostname'], device['location'])) logger.debug( 'Hostname: {} instanceUuid: {}'.format( device['hostname'], device_id)) device_provisioned = 1 else: logger.info('Adding device {} to site {}'.format( device['hostname'], device['location'])) data = common.build_json_from_template( templates.device_to_site_j2, {'device_ip': device_ip}) result = api.sites.assign_device_to_site( site_id, payload=data) status = common.wait_for_task_completion( api, result) logger.debug(status) device_provisioned = 1 if not device_provisioned: # If we didn't find a device under this site, try and assign the device to site # Again, not optimal as the device may exist under a different site logger.info( 'Device {} not found under target site {}'.format( device['hostname'], device['location'])) logger.info('Trying to add device {} to site {}'.format( device['hostname'], device['location'])) data = common.build_json_from_template( templates.device_to_site_j2, {'device_ip': device_ip}) result = api.sites.assign_device_to_site(site_id, payload=data) status = common.wait_for_task_completion(api, result) logger.debug(status) else: logger.error('schema not found: {}'.format(_schema))
def create_network_profile(api, workflow_dict): """ Creates switching Network Profiles. Wireless profile creation is handled in wireless module. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of network profiles (see schema.py); :returns: Nothing """ _schema = 'network_profiles.schema.network_profiles' logger.info('network_profiles::create_network_profiles') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # get profiles to be created new_profiles = [profile for profile in table_data if profile['presence'] == 'present'] if new_profiles: # check for dupe profile names result = api.custom_caller.call_api('GET', network_profile_base_url, params={"namespace": "switching"}) current_profiles = result['response'] current_profile_names = [profile['name'] for profile in current_profiles] # get site ids sites = api.sites.get_site() # now create it for profile in new_profiles: if profile['name'] in current_profile_names: logger.info('network_profiles:: {} already exists'.format(profile['name'])) pass else: logger.info('network profiles: Creating {}'.format(profile['name'])) data = common.build_json_from_template(templates.add_profile_j2, profile) result = api.custom_caller.call_api('POST', network_profile_base_url, json=data) logger.debug(result) create_result = common.wait_for_task_completion(api, result['response']) # if successful creation lets move on to adding sites if create_result['response']['isError'] == False: data = api.custom_caller.call_api('GET', network_profile_base_url, params={"name": profile['name']}) profile['id'] = data['response'][0]['siteProfileUuid'] # if location then assign it to profile # split up sites if csv if profile['sites']: logger.info('network_profiles::update_site') profile['sites'] = common.csv_string_to_list(profile['sites']) # get site id and add it to profile for new_site in profile['sites']: site_id = common.get_object_id(sites['response'], siteNameHierarchy=new_site) site_add_url = "{}/{}/site/{}".format(network_profile_base_url, profile['id'], site_id) result = api.custom_caller.call_api('POST', site_add_url) logger.debug(result) common.wait_for_task_completion(api, result['response']) # Assign templates to profile # first build out objects needed starting with day0 if profile['day0Template'] or profile['cliTemplate']: if profile['day0Template']: day0_list = common.csv_string_to_list(profile['day0Template']) day0_obj = [] if profile['product_series']: template_db = api.template_programmer.gets_the_templates_available( product_family="Switches and Hubs", product_series=profile['product_series'] ) else: template_db = api.template_programmer.gets_the_templates_available( productFamily="Switches and Hubs") # build object with names/ids for template in day0_list: for item in template_db: if item['name'] == template: # Get latest version version_list = [version['version'] for version in item['versionsInfo']] version = max(version_list) # Update Object day0_obj.append( {"name": item['name'], "id": item['templateId'], "version": version}) profile['day0Template'] = day0_obj # now build day 1 objects if profile['cliTemplate']: day1_list = common.csv_string_to_list(profile['cliTemplate']) day1_obj = [] if profile['product_series']: template_db = api.template_programmer.gets_the_templates_available( product_family="Switches and Hubs", product_series=profile['product_series'] ) else: template_db = api.template_programmer.gets_the_templates_available( product_family="Switches and Hubs") # build object with names/ids for template in day1_list: for item in template_db: if item['name'] == template: # Get latest version version_list = [version['version'] for version in item['versionsInfo']] version = max(version_list) # Update Object day1_obj.append( {"name": item['name'], "id": item['templateId'], "version": version}) # update profile with additional info needed (id,version #) profile['cliTemplate'] = day1_obj logger.info('network profiles::adding templates') data = common.build_json_from_template(templates.add_templates_to_profile_j2, profile) add_template_profile_url = "{}/{}".format(network_profile_base_url, profile['id']) result = api.custom_caller.call_api('PUT', add_template_profile_url, json=data) logger.debug(result) common.wait_for_task_completion(api, result['response']) else: logger.info('network profiles:: No Templates to add') else: logger.error('schema not found: {}'.format(_schema))
def create_wireless_interface(api, workflow_dict): # consider moving this as a child function """ Creates DNA Center Wireless Over The Top (OTT) Interfaces. If using OTT, wireless interfaces will need to be created before assigning them to a wireless profile :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of Wireless info with associated parameters. (see schema.yaml); :returns: Nothing """ _schema = 'ssid_to_profile_mapping.schema.wireless' logger.info('wireless::create_wireless_interface') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # create list of to be added interface mappings if they do not exist in current mappings new_interface_mappings = [ { "interfaceName": interface['interfaceNameOTT'], "vlanId": interface['ottVlan'] } for interface in table_data if interface['interfaceNameOTT'] is not None and interface['presence'] == "present" ] # if new interfaces proceed to adding new interfaces if new_interface_mappings: # get current wireless interfaces as PUT is not supported so we need to post all interfaces when adding a # new one result = api.custom_caller.call_api('GET', wireless_int_url) current_wireless_interfaces = result['response'][0]['value'] # create list of current interface mappings current_interface_mappings = [ { "interfaceName": interface['interfaceName'], "vlanId": interface['vlanId'] } for interface in current_wireless_interfaces if interface['interfaceName'] is not None ] current_int_names = [current_interface['interfaceName'] for current_interface in current_interface_mappings] # add new interfaces to current list for interface in new_interface_mappings: if interface["interfaceName"] not in current_int_names: current_interface_mappings.append( {"interfaceName": interface['interfaceName'], "vlanId": interface['vlanId']}) # format list and send create request current_interface_mappings = json.dumps(current_interface_mappings) logger.info('Creating Wireless Interfaces ') data = common.build_json_from_template(templates.wireless_interface_j2, current_interface_mappings) result = api.custom_caller.call_api('POST', wireless_int_url, json=data) logger.debug(result) common.wait_for_task_completion(api, result['response']) else: logger.info('Creating Wireless Interfaces: No Wireless interfaces to Create') pass else: logger.error('schema not found: {}'.format(_schema))
def create_templates(api, workflow_dict): """ Creates Templates. Template content can either be added to template content field or templates can be added to sample templates directory under templates module. If using templates json file, templateName field will need to match the name of template json identified in the __TEMPLATE_CONTENTS__ global variable. New template json files will need to be added to the __TEMPLATE_CONTENTS__. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of templates (see schema.py); :returns: Nothing """ _logInfo(inspect.currentframe().f_code.co_name) _schema = 'templates.schema.templates' logger.info('templates::create_templates') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] projects = _listTemplateProjects(api) for row in table_data: if _PRESENT_ in row[_PRESENCE_ROW_]: templateName = row[_TEMPLATES_TABLE_TEMPLATE_NAME_COLUMN_] templateDescription = row[ _TEMPLATES_TABLE_TEMPLATE_DESCRIPTION_COLUMN_] projectName = row[_TEMPLATES_TABLE_PROJECT_NAME_COLUMN_] _logDebug(("ProjectName: {}; TemplateName: {}").format( projectName, templateName)) projectID = _getProjectID(api, projectName) existing_template = _getTemplateInProjectId( api, templateName, projectID) _logDebug(("ProjectID: {}; Existing TemplateID:{}").format( projectID, existing_template)) # pprint.pprint(existing_template) if templateName not in _TEMPLATE_CONTENTS_.keys(): # if project exists create template if projectID: row['projectId'] = projectID # row['parentTemplateId'] = existing_template.id row['deviceTypes'] = [] row['deviceTypes'].append( {"productFamily": row['productFamily']}) row['deviceTypes'] = json.dumps(row['deviceTypes']) # check if template already exists if... if it does then use update if existing_template is not None: row['templateId'] = existing_template.id logger.info( "templates::create templates {} template exists, updating" .format(row['templateName'])) data = common.build_json_from_template( json_templates.blank_template_j2, row) response = api.template_programmer.update_template( projectID, payload=data) logger.debug(response) template_data = common.wait_for_task_completion( api, response['response']) # commit template to new version if template was created correctly if template_data['response']['data']: templateID = template_data['response']['data'] response = api.template_programmer.version_template( templateId=template_data['response'] ['data']) common.wait_for_task_completion( api, response['response']) logger.info( "border_handoff::{} template update successful" .format(row['templateName'])) return { "response": { "description": "Template Created Successfully", "isError": False, "templateId": templateID } } else: logger.error( "templates:: create {} template failed". format(row['templateName'])) # Otherwise create a new one else: logger.info( "templates::create templates:: create {} template" .format(row['templateName'])) data = common.build_json_from_template( json_templates.blank_template_j2, row) response = api.template_programmer.create_template( projectID, payload=data) logger.debug(response) template_data = common.wait_for_task_completion( api, response['response']) # commit template to version 1 if template was created correctly if template_data['response']['data']: templateId = template_data['response']['data'] response = api.template_programmer.version_template( templateId=templateId) common.wait_for_task_completion( api, response['response']) return { "response": { "description": "Template Created Successfully", "isError": False, "templateId": templateId } } else: return { "response": { "description": "Template Not Created", "isError": True } } # if existing template files exist generate template from json file else: template_content = _getTemplateContent(templateName) template_content["description"] = templateDescription if projectID is not None: if existing_template is None: # Create a new template _logDebug("Creating new template: " + templateName) response = api.template_programmer.create_template( **template_content, project_id=projectID) templateID = common.wait_on_task( api, response, "Create template: " + templateName) _logDebug(templateID) response = api.template_programmer.version_template( templateId=templateID) _logDebug(str(response)) return { "response": { "description": "Template Created Successfully", "isError": False, "templateId": templateID } } else: # Update existing template _logDebug("Updating existing template: " + existing_template.name) template_content['projectId'] = projectID template_content['id'] = existing_template.id template_content[ 'parentTemplateId'] = existing_template.id # pprint.pprint(template_content) response = api.template_programmer.update_template( **template_content) templateID = common.wait_on_task( api, response, "Update template: " + existing_template.name) return { "response": { "description": "Template Updated Successfully", "isError": False, "templateId": templateID } } else: _logInfo(templateName + " -> Project does not exist.") else: logger.error('schema not found: {}'.format(_schema))
def add_port_assignment(api, workflow_dict): """ FIXME. :param api: An instance of the dnacentersdk.DNACenterAPI class :param workflow_dict: A dictionary containing rows of discovery job definitions (see schema.yaml); :returns: Nothing """ _schema = 'ports.schema.host_onboarding' logger.info('host onboarding::add port assignment') logger.debug('schema: {}'.format(_schema)) if _schema in workflow_dict.keys(): table_data = workflow_dict[_schema] # get info from db port_assignments = [ assignment for assignment in table_data if assignment['presence'] == 'present' ] if port_assignments: for assignment in port_assignments: # provision AP Port if assignment['portType'] and assignment['portType'] == "ap": # check to make sure pool is of AP Provisioning segment_db = api.custom_caller.call_api( 'GET', base_sda_segment_uri)['response'] ap_provision_segments = [ segment['name'] for segment in segment_db if segment['isApProvisioning'] == True ] # if true assign the port if assignment['dataPoolName'] in ap_provision_segments: logger.info( 'host onboarding: Assigning {} to AP Port on Switch {}' .format(assignment['interfaceName'], assignment['deviceIP'])) ap_port_uri = "{}/{}".format(add_port_uri_base, "access-point") data = common.build_json_from_template( templates.port_template_j2, assignment) result = api.custom_caller.call_api('POST', ap_port_uri, json=data) common.wait_for_task_completion(api, result) # rate limited after 5 calls so need to sleep temporarily if len(port_assignments) > 5: time.sleep(12) # provision User port elif assignment['portType'] and assignment[ 'portType'] == "user": logger.info( 'host onboarding: Assigning {} to User Port on Switch {}' .format(assignment['interfaceName'], assignment['deviceIP'])) user_port_uri = "{}/{}".format(add_port_uri_base, "user-device") data = common.build_json_from_template( templates.port_template_j2, assignment) result = api.custom_caller.call_api('POST', user_port_uri, json=data) common.wait_for_task_completion(api, result) # rate limited after 5 calls so need to sleep temporarily if len(port_assignments) > 5: time.sleep(12) else: logger.error('schema not found: {}'.format(_schema))