Beispiel #1
0
def checksum(session, fid, echo=False):
    """Gets a zoning transaction checksum

    :param session: Session object returned from brcdapi.pyfos_auth.login()
    :type session: dict
    :param fid: Logical FID number for the fabric of interest
    :type fid: int
    :param echo: If True, echoes any error messages to STD_OUT
    :type echo: bool
    :return: checksum
    :rtype: int, None
    :return: brcdapi_rest status object
    :rtype: dict
    """
    # Get the checksum - this is needed to save the configuration.
    obj = brcdapi_rest.get_request(session,
                                   'brocade-zone/effective-configuration', fid)
    if _is_error(
            obj,
            'Failed to get zone data from "brocade-zone/effective-configuration"',
            echo):
        return None, obj
    try:
        return obj.get('effective-configuration').get('checksum'), obj
    except:
        brcdapi_log.log('Failed to get checksum', echo)
        brcdapi_log.exception(pprint.pformat(obj, indent=4), echo)
        return None, pyfos_auth.create_error(
            brcdapi_util.HTTP_INT_SERVER_ERROR,
            brcdapi_util.HTTP_REASON_UNEXPECTED_RESP,
            'Missing effective-configuration/checksum')
Beispiel #2
0
def api_request(session, uri, http_method, content):
    """Interface in front of _api_request to handle retries when services are unavailable

    :param session: Session object returned from login()
    :type session: dict
    :param uri: full URI
    :type uri: str
    :param http_method: Method for HTTP connect.
    :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
    """
    global _MAX_RETRIES

    if uri is None:  # An error occurred in _format_uri()
        buf = 'Missing URI'
        brcdapi_log.exception(buf, True)
        return pyfos_auth.create_error(brcdapi_util.HTTP_BAD_REQUEST,
                                       'Missing URI', buf)
    obj = _api_request(session, uri, http_method, content)
    retry_count = _MAX_RETRIES
    retry_flag, wait_time = _retry(obj)
    while retry_flag and retry_count > 0:
        time.sleep(wait_time)
        obj = _api_request(session, uri, http_method, content)
        retry_count -= 1
        retry_flag, wait_time = _retry(obj)
    return obj
Beispiel #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
Beispiel #4
0
def modify_zone(session,
                fid,
                zone,
                add_members,
                del_members,
                in_add_pmembers=None,
                in_del_pmembers=None,
                echo=False):
    """Adds and removes members from a zone.

    :param session: Session object returned from brcdapi.pyfos_auth.login()
    :type session: dict
    :param fid: Logical FID number
    :type fid: int
    :param zone: Name of zone to be modified
    :type zone: str
    :param add_members: Members to add to the zone
    :type add_members: list
    :param del_members: Members to delete from the zone
    :type del_members: list
    :param add_pmembers: Principal members to add to zone. Only relevant for peer zones
    :type add_pmembers: list
    :param del_pmembers: Principal members to delete from a zone. Only relevant for peer zones
    :type del_pmembers: list
    :param echo: If True, echoes any error messages to STD_OUT
    :type echo: bool
    :return: brcdapi_rest status object
    :rtype: dict
    """
    add_pmembers = list() if in_add_pmembers is None else in_add_pmembers
    del_pmembers = list() if in_del_pmembers is None else in_del_pmembers

    # This method reads the zone to change, makes the modifications in a local object, and PATCHes the change. I'm
    # assuming the type of zone could be changed but this method is just changing the membership. See "Important Notes"
    control = {
        'principal-entry-name': {
            'add_mem': add_pmembers,
            'del_mem': del_pmembers
        },
        'entry-name': {
            'add_mem': add_members,
            'del_mem': del_members
        },
    }
    # Read in the current defined zone
    obj = brcdapi_rest.get_request(
        session, 'brocade-zone/defined-configuration/zone/zone-name/' + zone,
        fid)
    if _is_error(obj, 'Failed reading zone ' + zone, echo):
        return obj

    # Modify the zone
    d = obj.get('zone')
    if d is None:
        return pyfos_auth.create_error(
            brcdapi_util.HTTP_BAD_REQUEST,
            brcdapi_util.HTTP_REASON_MAL_FORMED_OBJ,
            'Missing leaf "zone" in returned object for ' + zone)
    me = d.get('member-entry')
    if me is None:
        me = dict(
        )  # I'm not sure what FOS returns if all the members were deleted so this is just to be safe
        d.update({'member-entry': me})
    for k, v in control.items():
        ml = me.get(k)
        if len(v.get('add_mem')) + len(v.get('del_mem')) > 0:
            if ml is None:
                ml = list(
                )  # Just a safety net. Similar to 'member-entry' above.
                me.update(
                    {k: ml}
                )  # If there are principle members to a non-peer zone, FOS returns an error
            for mem in v.get('add_mem'):
                ml.append(mem)
            for mem in v.get('del_mem'):
                try:
                    ml.remove(mem)
                except:
                    pyfos_auth.create_error(
                        brcdapi_util.HTTP_BAD_REQUEST, 'Delete error',
                        'Member ' + mem + ' does not exist')

    content = {'defined-configuration': obj}
    obj = brcdapi_rest.send_request(session,
                                    'brocade-zone/defined-configuration',
                                    'PATCH', content, fid)
    _is_error(obj, 'Failed to create zones', echo)
    return obj
Beispiel #5
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