예제 #1
0
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
예제 #2
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
예제 #3
0
파일: zone.py 프로젝트: jconsoli/brcdapi
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
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
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']
예제 #7
0
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