예제 #1
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
예제 #2
0
def get_chassis(session, proj_obj):
    """Gets the  chassis object and objects for all logical switches in the chassis.

    :param session: Session object returned from brcdapi.pyfos_auth.login()
    :type session: dict
    :param proj_obj: The project object
    :type proj_obj: brcddb.classes.project.ProjectObj
    :return: Chassis object
    :rtype: brcddb.classes.ChassisObj
    """
    # See if we already got it
    if 'chassis_wwn' in session:
        chassis_obj = proj_obj.r_chassis_obj(session.get('chassis_wwn'))
        if chassis_obj is not None:
            return chassis_obj

    # Go get it
    uri = 'brocade-chassis/chassis'
    obj = brcdapi_rest.get_request(session, uri, None)
    if pyfos_auth.is_error(obj):
        return None
    try:
        wwn = obj.get('chassis').get('chassis-wwn')
        chassis_obj = proj_obj.s_add_chassis(wwn)
        results_action(chassis_obj, obj, uri)
        session.update(dict(chassis_wwn=wwn))
        # Get all the switches in this chassis
        if chassis_obj.r_is_vf_enabled():  # Get all the logical switches in this chassis
            uri = 'brocade-fibrechannel-logical-switch/fibrechannel-logical-switch'
            obj = brcdapi_rest.get_request(session, uri, None)
            if pyfos_auth.is_error(obj):
                _process_errors(session, uri, obj, chassis_obj)
                fid_l = list()
            else:
                results_action(chassis_obj, obj, uri)
                fid_l = [d.get('fabric-id') for d in obj.get('fibrechannel-logical-switch')]
        else:
            fid_l = [None]
        for fid in fid_l:
            uri = 'brocade-fabric/fabric-switch'
            obj = brcdapi_rest.get_request(session, uri, fid)
            _process_errors(session, uri, obj, chassis_obj)
            results_action(chassis_obj, obj, uri)
        return chassis_obj
    except:
        _process_errors(session, uri, session, proj_obj)
        return None
예제 #3
0
def replace_zoning(session, fab_obj, fid):
    """Replaces the zoning datbase in a fabric by clearing it and then PATCHing it with a new zoning database

     Relevant resource: 'zoning/defined-configuration'
     An error is returned if there is no zone database in in the fab_obj. Use clear_zoning() to clear out zoning.

    :param session: Login session object from brcdapi.brcdapi_rest.login()
    :type session: dict
    :param fab_obj: Fabric object whose zone database will be sent to the switch
    :type fab_obj: brcddb.classes.fabric.FabricObj
    :param fid: Fabric ID. If FID check is disabled, this must be the FID of the switch where the request is sent.
    :type fid: int
    :return: Object returned from FOS API
    :rtype: dict
    """
    # Get the dict to be converted to JSON and sent to the switch
    content = build_all_zone_content(fab_obj)
    if content is None:
        return pyfos_auth.create_error(
            400, 'No zone database in ' +
            brcddb_fabric.best_fab_name(obj.r_fabric_obj()), '')

    # Get the checksum - this is needed to save the configuration.
    checksum, obj = brcdapi_zone.checksum(session, fid, fab_obj)
    if pyfos_auth.is_error(obj):
        return obj

    # Clear the zone database
    obj = brcdapi_zone.clear_zone(session, fid)
    if not pyfos_auth.is_error(obj):
        # Send the zoning request
        obj = brcdapi_rest.send_request(session,
                                        'brocade-zone/defined-configuration',
                                        'PATCH', content, fid)
        if not pyfos_auth.is_error(obj):
            return brcdapi_zone.save(session, fid, checksum)

    # If we got this far, something went wrong so abort the transaction.
    brcdapi_zone.abort(session, fid)
    return obj
예제 #4
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
예제 #5
0
def login(user_id, pw, ip_addr, https='none'):
    """Performs a login to the device using pyfos_auth.login

    :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: str
    :return: PyFOS session object
    :rtype: dict
    """
    if _DEBUG and _DEBUG_MODE == 1:
        return {'_debug_name': ip_addr.replace('.', '_'), 'debug': True}
    session = pyfos_auth.login(user_id, pw, ip_addr, https)
    if not pyfos_auth.is_error(session) and _DEBUG:
        session.update(dict(_debug_name=ip_addr.replace('.', '_')))
    return session
예제 #6
0
def results_action(brcddb_obj, fos_obj, kpi):
    """Updates the brcddb database for an API request response.

    :param brcddb_obj: brcddb class librairy object
    :type brcddb_obj: brcddb.classes.*
    :param fos_obj: Object returned from the API
    :type fos_obj: dict
    :param kpi: KPI associated with fos_obj
    :type kpi: str
    """
    global _custom_rest_methods, _rest_methods

    if not pyfos_auth.is_error(fos_obj):
        try:
            if kpi in _custom_rest_methods:
                _custom_rest_methods[kpi](brcddb_obj, fos_obj, kpi)
            else:
                _rest_methods[brcdapi_util.uri_map[kpi]['area']](brcddb_obj, fos_obj, kpi)
        except:
            buf = 'Could not add ' + kpi + ' to ' + str(type(brcddb_obj)) + '. This typially occurs when something '
            buf += 'for the fabric was polled but the fabric WWN is unkonwn.'
            brcdapi_log.log(buf, True)
예제 #7
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)
예제 #8
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
예제 #9
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']
예제 #10
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
예제 #11
0
파일: port.py 프로젝트: jconsoli/brcdapi
def default_port_config(session, fid, i_ports):
    """Disables and sets a list of FC ports to their factory default state

    :param session: Session object returned from brcdapi.pyfos_auth.login()
    :type session: dict
    :param fid: Logical FID number for switch with ports. Use None if switch is not VF enabled.
    :type fid: int
    :param i_ports: List of ports in the API format of s/p. For a fixed port switch for example, port 12 is '0/12'
    :type i_ports: tuple, list
    :return: The object returned from the API. If ports is an empty list, a made up good status is returned.
    :rtype: dict
    """
    global default_port_config_d

    ports = ports_to_list(i_ports)
    if len(ports) == 0:
        return brcdapi_util.GOOD_STATUS_OBJ

    # Not all features are supported on all platforms. In most cases, even if you disable the unsupported feature, FOS
    # returns an error. To get around this, I read the port configuration and only add parameters to send to the switch
    # if they exist in the data returned from the switch

    # Read in the port configurations
    obj = brcdapi_rest.get_request(session, 'brocade-interface/fibrechannel',
                                   fid)
    if pyfos_auth.is_error(obj):
        brcdapi_log.log(
            'Failed to read brocade-interface/fibrechannel for fid ' +
            str(fid), True)
        return obj
    # Put all the ports in a dictionary for easy lookup
    port_d = dict()
    for port in obj['fibrechannel']:
        port_d.update({port['name']: port})

    # Figure out what ports to change
    pl = list()
    for port in ports:
        d = port_d.get(port)
        if d is None:
            brcdapi_log.exception('Port ' + port + ' not in FID ' + str(fid),
                                  True)
            continue
        port_content = collections.OrderedDict(
        )  # This may not need to be ordered.
        port_content['name'] = port
        for k, v in default_port_config_d.items():
            if k in d:
                if k == 'speed':
                    if d.get('auto-negotiate'
                             ) is not None and d['auto-negotiate'] == 0:
                        port_content[k] = 0
                elif k == 'user-friendly-name':
                    l = port.split('/')
                    port_name = 'port' + l[1]
                    if l[0] != '0':
                        port_name = 'slot' + l[0] + ' ' + port_name
                    if 'user-friendly-name' in d:
                        if d['user-friendly-name'] != port_name:
                            port_content[k] = port_name
                    else:
                        port_content[k] = port_name
                elif v != d[k]:
                    port_content[k] = v
        if len(port_content.keys()) > 1:
            pl.append(port_content)

    # Now modify the port(s)
    if len(pl) > 0:
        return brcdapi_rest.send_request(session,
                                         'brocade-interface/fibrechannel',
                                         'PATCH', {'fibrechannel': pl}, fid)
    else:
        return brcdapi_util.GOOD_STATUS_OBJ
예제 #12
0
def _api_request(session, uri, http_method, content):
    """Single interface to the FOS REST API. Performs a Rest API request. Only tested with GET, PATCH, POST, and DELETE.

    :param session: PyFOS session object
    :type session: dict
    :param uri: full URI
    :type uri: str
    :param http_method: Method for HTTP connect. Only tested with 'PATCH' and 'POST'.
    :type http_method: str
    :param content: The content, in Python dict, to be converted to JSON and sent to switch.
    :type content: dict
    :return: Response and status in pyfos_auth.is_error() and pyfos_auth.formatted_error_msg() friendly format
    :rtype: dict
    """
    if verbose_debug:
        buf = [
            'api_request() - Send:', 'Method: ' + http_method, 'URI: ' + uri,
            'content:',
            pprint.pformat(content)
        ]
        brcdapi_log.log(buf, True)

    # Set up the headers and JSON data
    header = session.get('credential')
    header.update({'Accept': 'application/yang-data+json'})
    header.update({'Content-Type': 'application/yang-data+json'})
    conn = session.get('conn')

    # Send the request and get the response
    json_data = json.dumps(
        content) if content is not None and len(content) > 0 else None
    try:
        conn.request(http_method, uri, json_data, header)
    except:
        obj = pyfos_auth.create_error(
            brcdapi_util.HTTP_NOT_FOUND, 'Not Found',
            'Typical of switch going offline or pre-FOS 8.2.1c')
        if 'ip_addr' in session:
            obj.update(dict(ip_addr=session.get('ip_addr')))
        return obj
    try:
        json_data = pyfos_auth.basic_api_parse(conn.getresponse())
        if verbose_debug:
            brcdapi_log.log(
                ['api_request() - Response:',
                 pprint.pformat(json_data)], True)
    except:
        buf = 'Time out processing ' + uri + '. Method: ' + http_method
        brcdapi_log.log(buf, True)
        obj = pyfos_auth.create_error(brcdapi_util.HTTP_REQUEST_TIMEOUT, buf,
                                      '')
        return obj

    # Do some basic parsing of the response
    tl = uri.split('?')[0].split('/')
    cmd = tl[len(tl) - 1]
    if pyfos_auth.is_error(json_data):
        try:
            msg = json_data['errors']['error']['error-message']
        except:
            try:
                # The purpose of capturing the message is to support the code below that works around a defect in FOS
                # whereby empty lists or no change PATCH requests are returned as errors. In the case of multiple
                # errors, I'm assuming the first error is the same for all errors. For any code I wrote, that will be
                # true. Since I know this will be fixed in a future version of FOS, I took the easy way out.
                msg = json_data['errors']['error'][0]['error-message']
            except:
                msg = ''
        try:
            if http_method == 'GET' and json_data['_raw_data']['status'] == brcdapi_util.HTTP_NOT_FOUND and \
                    json_data['_raw_data']['reason'] == 'Not Found':
                ret_obj = dict(cmd=list())  # It's really just an empty list
            elif http_method == 'GET' and json_data['_raw_data']['status'] == brcdapi_util.HTTP_BAD_REQUEST and \
                    msg == 'No entries in the FDMI database':
                ret_obj = dict(cmd=list())  # It's really just an empty list
            elif http_method == 'GET' and json_data['_raw_data']['status'] == brcdapi_util.HTTP_BAD_REQUEST and \
                     json_data['_raw_data']['reason'] == 'Bad Request' and 'Not supported on this platform' in msg:
                ret_obj = dict(cmd=list())  # It's really just an empty list
            elif http_method == 'PATCH' and json_data['_raw_data']['status'] == brcdapi_util.HTTP_BAD_REQUEST and \
                    json_data['_raw_data']['reason'] == 'Bad Request' and \
                    ('No Change in Configuration' in msg or 'Same configuration' in msg):
                # Sometimes FOS 8.2.1 returns no change as this error and sometimes it doesn't. Expected fix for no
                # change with PATCH is to always return good status (204). Note that according to RFC 5789, no change is
                # not considered an error.
                ret_obj = dict(cmd=list())
            else:
                ret_obj = json_data
        except:
            try:
                status = json_data['_raw_data']['status']
            except:
                status = 0
                msg = 'No status provided.'
            try:
                reason = json_data['_raw_data']['reason']
            except:
                reason = 'No reason provided'
            ret_obj = pyfos_auth.create_error(status, reason, msg)
    elif 'Response' in json_data:
        obj = json_data.get('Response')
        ret_obj = obj if bool(obj) else {cmd: list()}
    else:
        raw_data = json_data.get('_raw_data')
        if raw_data is not None:
            status = brcdapi_util.HTTP_BAD_REQUEST if raw_data.get(
                'status') is None else raw_data.get('status')
            reason = '' if raw_data.get('reason') is None else raw_data.get(
                'reason')
        else:
            status = brcdapi_util.HTTP_BAD_REQUEST
            reason = 'Invalid response from the API'
        if status < 200 or status >= 300:
            ret_obj = pyfos_auth.create_error(status, reason, '')
        else:
            ret_obj = dict()

    return ret_obj