def _retry(obj): """Determines if a request should be retried. :param obj: Object returned from _api_request() :type obj: dict :return retry_flag: True - request should be retried. False - request should not be retried. :rtype retry_flag: bool :return delay: Time, in seconds, to wait for retrying the request :rtype delay: int """ status = pyfos_auth.obj_status(obj) reason = pyfos_auth.obj_reason(obj) if isinstance( pyfos_auth.obj_reason(obj), str) else '' if isinstance(status, int) and status == 503 and isinstance( reason, str) and 'Service Unavailable' in reason: brcdapi_log.log( 'FOS API services unavailable. Will retry in ' + str(_SVC_UNAVAIL_WAIT) + ' seconds.', True) return True, _SVC_UNAVAIL_WAIT if status == brcdapi_util.HTTP_BAD_REQUEST and 'The Fabric is busy' in pyfos_auth.formatted_error_msg( obj): brcdapi_log.log('Fabric is busy. Will retry in ' + str(_FABRIC_BUSY_WAIT) + ' seconds.') return True, _FABRIC_BUSY_WAIT return False, 0
def login(user_id, pw, ip_addr, https='none', proj_obj=None): """Log in and capture a list of supported modules. :param user_id: User ID :type user_id: str :param pw: Password :type pw: str :param ip_addr: IP address :type ip_addr: str :param https: If 'CA' or 'self', uses https to login. Otherwise, http. :type https: None, str :param proj_obj: Project object :type proj_obj: brcddb.classes.project.ProjectObj, None :return: session :rtype: dict """ # Attempt to log in tip = brcdapi_util.mask_ip_addr(ip_addr, True) session = brcdapi_rest.login(user_id, pw, ip_addr, https) if pyfos_auth.is_error(session): brcdapi_log.log(tip + ' Login failed', True) _process_errors(session, '/rest/login', session, proj_obj) return session else: brcdapi_log.log(tip + ' Login succeeded', True) # Figure out what modules this FOS version supports # Step 1: Read all the module information from FOS uri = 'brocade-module-version' obj = brcdapi_rest.get_request(session, uri) if pyfos_auth.is_error(obj): brcdapi_log.log(tip + ' ERROR: ' + uri + '\n' + pyfos_auth.formatted_error_msg(obj), True) return session # Step 2: Parse the module information and add it to the session object kpi_list = list() for mod_obj in obj.get(uri).get('module'): name = mod_obj.get('name') if name is None: brcdapi_log.exception("'name' mising in brocade-module-version", True) continue kpi_list.append(name) try: # I think 'object' will always be there, just empty when there are no objects. Try/Except just in case. for mod_object in mod_obj.get('objects').get('object'): kpi_list.append(name + '/' + mod_object) except: pass session.update(dict(supported_uris=kpi_list)) return session
def _is_error(obj, msg, echo): """Updates the log file if there was an error :param obj: Object returned from API :type obj: dict :param msg: Message to add to the log buffer :type msg: str :param echo: True - echo message to STD_OUT :type echo: bool :return: Version :rtype: str """ if pyfos_auth.is_error(obj): brcdapi_log.log(msg, echo) brcdapi_log.exception(pyfos_auth.formatted_error_msg(obj), echo) return True return False
def _process_errors(session, uri, obj, wobj): """Checks for errors in the API response and adds alerts to the object if there is an error :param session: Session object returned from brcdapi.pyfos_auth.login() :type session: dict :param uri: URI, less the prefix :type uri: str :param obj: FOS API object :type obj: dict :param wobj: The brcddb class object we were working when the error occured :type wobj: brcddb.classes.project.ProjectObj, brcddb.classes.chassis.ChassisObj, brcddb.classes.switch.SwitchObj,\ None """ if not pyfos_auth.is_error(obj): return ip_addr = brcdapi_util.mask_ip_addr(session.get('ip_addr')) p1 = pyfos_auth.formatted_error_msg(obj) brcdapi_log.log(ip_addr + " Request FAILED:\n" + p1, True) if wobj is None: return proj_obj = wobj.r_project_obj() proj_obj.s_api_error_flag() error_table = dict( ProjectObj=dict( p0=ip_addr, al_num=al.ALERT_NUM.PROJ_FAILED_LOGIN if '/rest/login' in uri else al.ALERT_NUM.PROJ_CHASSIS_API_ERROR ), ChassisObj=dict(p0=brcddb_chassis.best_chassis_name(wobj), al_num=al.ALERT_NUM.PROJ_CHASSIS_API_ERROR), SwitchObj=dict(p0=brcddb_switch.best_switch_name(wobj), al_num=al.ALERT_NUM.PROJ_SWITCH_API_ERROR) ) simple_type = brcddb_class_util.get_simple_class_type(wobj) if simple_type in error_table: p0 = error_table[simple_type]['p0'] al_num = error_table[simple_type]['al_num'] else: brcdapi_log.exception('Unknown object type: ' + str(type(wobj))) return proj_obj.s_add_alert(al.AlertTable.alertTbl, al_num, None, p0, p1)
def pseudo_main(): """Basically the main(). :return: Exit code :rtype: int """ ec = 0 # Get and condition the command line input ml = ['WARNING!!! Debug is enabled'] if _DEBUG else list() ip, user_id, pw, sec, fid, name, did, idid, xisl, base, ficon, i_ports, i_ge_ports, es, ep, echo, vd = parse_args( ) if vd: brcdapi_rest.verbose_debug = True if sec is None: sec = 'none' ml.append('Access: HTTP') else: ml.append('Access: HTTPS') try: fid = int(fid) if fid < 1 or fid > 128: raise except: brcdapi_log.log('Invalid fid. FID must be an integer between 1-128', True) return -1 if did is not None: try: did = int(did) if did < 1 or did > 239: raise except: brcdapi_log.log( 'Invalid DID. DID must be an integer between 1-239', True) return -1 ml.append('FID: ' + str(fid)) ml.append('Name: ' + str(name)) ml.append('DID: ' + str(did)) ml.append('Insistent: ' + str(idid)) ml.append('xisl: ' + str(xisl)) ml.append('base: ' + str(base)) ml.append('ficon: ' + str(ficon)) ml.append('ports: ' + str(i_ports)) ml.append('ge_ports: ' + str(i_ge_ports)) ml.append('Enable switch: ' + str(es)) ml.append('Enable ports: ' + str(ep)) brcdapi_log.log(ml, True) base = False if base is None else base ficon = False if ficon is None else ficon es = False if es is None else es ep = False if ep is None else ep echo = False if echo is None else echo # Login brcdapi_log.log('Attempting login', True) session = brcdapi_rest.login(user_id, pw, ip, sec) if pyfos_auth.is_error(session): brcdapi_log.log([ 'Login failed. API error message is:', pyfos_auth.formatted_error_msg(session) ], True) return -1 brcdapi_log.log('Login succeeded.', True) try: # I always do a try in code development so that if there is a code bug, I still log out. # Get a list of ports to move to the new switch port_d, ge_port_d = _parse_ports(session, fid, i_ports, i_ge_ports, echo) # We're done with conditioning the user input. Now create the logical switch. ec = create_ls(session, fid, name, did, idid, xisl, base, ficon, port_d, ge_port_d, es, ep, echo) except: brcdapi_log.log('Encountered a programming error', True) ec = -1 # Logout obj = brcdapi_rest.logout(session) if pyfos_auth.is_error(obj): brcdapi_log.log([ 'Logout failed. API error message is:', pyfos_auth.formatted_error_msg(obj) ], True) return ec
def _parse_ports(session, fid, i_ports, i_ge_ports, echo): """This method has nothing to do with configuring switches. It is for parsing the user input used in this module only. It creates a dictionary, key is the FID number and value is the list of ports. This dictionary is used in ls_create() to determine the ports associated with each FID that is to be moved to the new FID. :param session: Session object returned from brcdapi.pyfos_auth.login() :type session: dict :param fid: Logical FID number to be created. :type fid: int :param i_ports: The value for -ports as it was passed in the command shell. :type i_ports: str :param i_ge_ports: The value for -ge_ports as it was passed in the command shell. :type i_ge_ports: str :param echo: If True, step-by-step activity (each request) is echoed to STD_OUT :type echo: None, bool :return port_d: Dictionary whose key is the FID and value a list of ports in that FID to be moved :rtype port_d: dict :return ge_port_d: Dictionary whose key is the FID and value a list of ports in that FID to be moved :rtype ge_port_d: dict """ """This module and it's user interface was intended as a means to provide an example of how to use the library, not an end all module for configuring switches. As such, the user interface is limited to contiguous slots and ports; however, the library accepts a list of ports in any order. When moving ports via the CLI, lscfg --config xxx -port y, the ports being moved cannot have any special configuration parameters. Otherwise, an error is returned and the user must set the ports to their default configuration. To change the port configurations, the user must know what FID each port is in. The library must do the same thing. Automation programs do not have a user so the program must be able to figure out where the ports are. The brcdapi_switch.add_ports() method checks the configuration of each port and, if necessary, sets them to their default configuration before moving them. The input to brcdapi_switch.add_ports() is simplistic. The caller is responsible for figuring out where the ports to move are. It takes two dictionaries, one for standard FC ports and the other for IP ports, as input. In each dictionary, the key is the FID number and the value is the list of ports, in standard s/p format. Your code can derive the list of ports anyway it wants. The user interface was just a mechanism to illustrate how to use the API for creating a logical switch so not much time was spent developing it. You could use switch_add_ports.py to add additional ports as a quick solution to creating logical switches but keep in mind that the primary intent of this script is to provide programming examples. The code below does the following: 1. Creates a list of ports in s/p notation that matches the user input. 2. Reads all the ports for all FIDs in the chassis 3. Builds a dictionary whose key if the FID number and whose value is the list of ports in that FID that are also in the list created in step 1. This is the structure that is passed to create_ls() """ port_d = dict(ports=dict(input=i_ports, ref='port-member-list', port_l=list(), ports=dict()), ge_ports=dict(input=i_ge_ports, ref='ge-port-member-list', port_l=list(), ports=dict())) # Step 1: Create list of ports that match the user input if isinstance(i_ports, str) or isinstance(i_ge_ports, str): for k, d in port_d.items(): if isinstance(d['input'], str): tl = d['input'].split('/') if len(tl) == 1: input_slots = '0' input_ports = tl[0] else: input_slots = tl[0] input_ports = tl[1] tl = input_slots.split('-') slot_l = tl[0] if len(tl) == 1 else [ str(i) for i in range(int(tl[0]), int(tl[1]) + 1) ] tl = input_ports.split('-') port_l = tl[0] if len(tl) == 1 else [ str(i) for i in range(int(tl[0]), int(tl[1]) + 1) ] for s in slot_l: d['port_l'].extend([s + '/' + p for p in port_l]) # Step 2: Read the port list for each FID switch_list = brcdapi_switch.logical_switches(session, echo) if pyfos_auth.is_error(switch_list): brcdapi_log.log([ 'Error capturing switch list. Ports not added to FID ' + str(fid), pyfos_auth.formatted_error_msg(switch_list) ], True) # Step 3: Build the dictionaries for input to brcdapi_switch.add_ports() else: # Build the dictionaries with the list of ports that match the user input by FID. for k, d in port_d.items(): for switch_d in switch_list: x_fid = switch_d['fabric-id'] # We haven't created the logical switch yet so x_fid will never == fid in the test below. This # check is here to remind programmers who are using this module as an example of how to build a # switch that you may need to perform this check. FOS will return an error if you try to move a # port to the FID the port already is in. if x_fid != fid: tl = list() if switch_d[d['ref']].get('port-member') is None else \ switch_d[d['ref']].get('port-member') d['ports'].update( {x_fid: [p for p in tl if p in d['port_l']]}) return port_d['ports']['ports'], port_d['ge_ports']['ports']
def create_ls(session, fid, name, did, idid, xisl, base, ficon, ports, ge_ports, es, ep, echo): """Create a logical switch. Includes options to set a few basic parameters :param session: Session object returned from brcdapi.pyfos_auth.login() :type session: dict :param fid: Logical FID number to be created. :type fid: int :param name: User friendly name for the switch. Use None for FOS default switch name :type name: None, str :param did: Domain ID Use None for FOS default DID. :type did: None, int :param idid: If True, sets the DID to be insistent :type idid: None, bool :param xisl: If True, sets the allow XISL bit. :type xisl: None, bool :param base: If True, set the switch type to be a base switch :type base: None, bool :param ficon: If True set the switch type to be a FICON switch :type ficon: None, bool :param ports: Dictionary of ports to add to the switch. Key: FID where the ports reside. Value: list of ports :type ports: None, dict :param ge_ports: Same as ports but for GE ports :type ge_ports: None, dict :param es: If True, enable the switch. Otherwise, the switch is left disabled. :type es: None, bool :param ep: If True, enables all the ports in the switch. Otherwise, they are left disabled. :type ep: None, bool :param echo: If True, step-by-step activity (each request) is echoed to STD_OUT :type echo: None, bool :return: Error code. 0 - Success, -1 - failure :rtype: int """ ec = 0 # Does the switch already exist? switch_list = brcdapi_switch.logical_switches(session, echo) if pyfos_auth.is_error(switch_list): brcdapi_log.log([ 'Error capturing switch list. Ports not added to FID ' + str(fid), pyfos_auth.formatted_error_msg(switch_list) ], True) return -1 fid_list = [switch_d['fabric-id'] for switch_d in switch_list] # Create the switch if it doesn't already exist if fid in fid_list: brcdapi_log.log('Modifying FID ' + str(fid)) else: buf = 'Creating FID ' + str( fid ) + '. This will take about 20 sec per switch + 25 sec per group of 32 ports.' brcdapi_log.log(buf, True) obj = brcdapi_switch.create_switch(session, fid, base, ficon, echo) if pyfos_auth.is_error(obj): brcdapi_log.exception([ 'Error creating FID ' + str(fid), pyfos_auth.formatted_error_msg(obj) ], True) return -1 # Set switch configuration parameters. Note: brocade-fibrechannel-switch/fibrechannel-switch requires the WWN and # must be an ordered dictionary. To save a little work, the ability to look up the WWN if it's not known and # setting up an ordered dictionary was built into one of the driver methods. sub_content = dict() if did is not None: if fid in fid_list: brcdapi_log.log( 'Cannot modify the domain ID in an existing switch.', True) else: sub_content.update({'domain-id': did}) if name is not None: sub_content.update({'user-friendly-name': name}) if ficon: sub_content.update({ 'in-order-delivery-enabled': True, 'dynamic-load-sharing': 'two-hop-lossless-dls' }) # I didn't bother with a fabric name or banner in the shell interface. I have no idea why the fabric name is set and # read in the switch parameters, but it is. if _DEBUG_FAB_NAME is not None: sub_content.update({'fabric-user-friendly-name': _DEBUG_FAB_NAME}) if _DEBUG_BANNER is not None: sub_content.update({'banner': _DEBUG_BANNER}) # If there is nothing to update, the library will do nothing and return good status. obj = brcdapi_switch.fibrechannel_switch(session, fid, sub_content, None, echo) if pyfos_auth.is_error(obj): brcdapi_log.exception([ 'Failed to configure FID ' + str(fid), pyfos_auth.formatted_error_msg(obj) ], True) ec = -1 # Set the fabric parameters. Note: Setting up fabric parameters is pretty straight forward so there is no driver # method for it. if fid in fid_list: brcdapi_log.log( 'Changing XISL use in an existing switch is not supported by this utility.', True) elif not xisl: # XISL (ability to use the base switch for ISLs) is enabled by default so we only need to disable it obj = brcdapi_rest.send_request( session, 'brocade-fibrechannel-configuration/switch-configuration', 'PATCH', {'switch-configuration': { 'xisl-enabled': False }}, fid) if pyfos_auth.is_error(obj): ml = [ 'Failed to disable XISL for FID ' + str(fid), pyfos_auth.formatted_error_msg(obj), 'Enabling and disabling of XISLs via the API was not supported until FOS v9.0.', 'Unless there are other error messages, all other operations are or will be completed as expected.' ] brcdapi_log.exception(ml, True) ec = -1 # Insistent domain ID is set automatically with FICON switches. The API returns an error if you try to set it. if idid and not ficon: obj = brcdapi_rest.send_request( session, 'brocade-fibrechannel-configuration/fabric', 'PATCH', {'fabric': { 'insistent-domain-id-enabled': True }}, fid) if pyfos_auth.is_error(obj): brcdapi_log.exception([ 'Failed to set insistent domain id for FID ' + str(fid), pyfos_auth.formatted_error_msg(obj) ], True) ec = -1 # Add the ports to the switch. This has to be done one FID at a time. tl = list( ports.keys() ) # The keys are the FIDs so this is the list of all FIDs that have ports to be moved. tl.extend([k for k in ge_ports.keys() if k not in tl]) # Add FIDs for GE ports for k in tl: # For every FID with ports to move obj = brcdapi_switch.add_ports(session, fid, k, ports.get(k), ge_ports.get(k), echo) if pyfos_auth.is_error(obj): brcdapi_log.log([ 'Error adding ports from FID ' + str(k), pyfos_auth.formatted_error_msg(obj) ], True) ec = -1 # Enable the switch if es is not None and es: obj = brcdapi_switch.fibrechannel_switch(session, fid, {'is-enabled-state': True}, None, echo) if pyfos_auth.is_error(obj): brcdapi_log.log([ 'Failed to enable FID ' + str(fid), pyfos_auth.formatted_error_msg(obj) ], True) ec = -1 # Enable the ports we just added if ep is not None and ep: for k in tl: obj = brcdapi_port.enable_port(session, fid, True, ports.get(k) + ge_ports.get(k), True) if pyfos_auth.is_error(obj): brcdapi_log.log([ 'Failed to enable ports on FID ' + str(fid), pyfos_auth.formatted_error_msg(obj) ], True) ec = -1 return ec