Ejemplo n.º 1
0
    def _run_eos_cmds(self, commands, commands_to_log=None):
        """Execute/sends a CAPI (Command API) command to EOS.

        In this method, list of commands is appended with prefix and
        postfix commands - to make is understandble by EOS.

        :param commands : List of command to be executed on EOS.
        :param commands_to_log : This should be set to the command that is
                                 logged. If it is None, then the commands
                                 param is logged.
        """

        if self._server is None:
            self._server_ip = self._get_eos_master()
            if self._server_ip is None or self._server is None:
                msg = "Failed to identify EOS master"
                raise arista_exc.AristaRpcError(msg=msg)

        log_cmds = commands
        if commands_to_log:
            log_cmds = commands_to_log

        LOG.info(_LI('Executing command on Arista EOS: %s'), log_cmds)

        try:
            # this returns array of return values for every command in
            # full_command list
            ret = self._server.runCmds(version=1, cmds=commands)
        except Exception as error:
            error_msg_str = unicode(error)
            if commands_to_log:
                # The command might contain sensitive information. If the
                # command to log is different from the actual command, use
                # that in the error message.
                for cmd, log_cmd in itertools.izip(commands, log_cmds):
                    error_msg_str = error_msg_str.replace(cmd, log_cmd)
            msg = (_('Error %(err)s while trying to execute '
                     'commands %(cmd)s on EOS %(host)s') %
                   {'err': error_msg_str,
                    'cmd': commands_to_log,
                    'host': self._server_ip})

            # Reset the server as we failed communicating with it;
            # there might just be another master
            self._server = None
            self._server_ip = None

            # Logging exception here can reveal passwords as the exception
            # contains the CLI command which contains the credentials.
            LOG.error(msg)
            raise arista_exc.AristaRpcError(msg=msg)

        return ret
Ejemplo n.º 2
0
 def _send_api_request(self, path, method, data=None, sanitized_data=None):
     host = self._get_eos_master()
     if not host:
         msg = six.text_type("Could not find CVX leader")
         LOG.info(msg)
         self.set_cvx_unavailable()
         raise arista_exc.AristaRpcError(msg=msg)
     self.set_cvx_available()
     return self._send_request(host, path, method, data, sanitized_data)
Ejemplo n.º 3
0
    def _run_eos_cmds(self, commands, commands_to_log=None):
        """Execute/sends a CAPI (Command API) command to EOS.

        In this method, list of commands is appended with prefix and
        postfix commands - to make is understandble by EOS.

        :param commands : List of command to be executed on EOS.
        :param commands_to_log : This should be set to the command that is
                                 logged. If it is None, then the commands
                                 param is logged.
        """

        # Always figure out who is master (starting with the last known val)
        try:
            if self._get_eos_master() is None:
                msg = "Failed to identify CVX master"
                self.set_cvx_unavailable()
                raise arista_exc.AristaRpcError(msg=msg)
        except Exception:
            self.set_cvx_unavailable()
            raise

        self.set_cvx_available()
        log_cmds = commands
        if commands_to_log:
            log_cmds = commands_to_log

        LOG.info(_LI('Executing command on Arista EOS: %s'), log_cmds)
        # this returns array of return values for every command in
        # full_command list
        try:
            response = self._send_eapi_req(cmds=commands,
                                           commands_to_log=log_cmds)
            if response is None:
                # Reset the server as we failed communicating with it
                self._server_ip = None
                self.set_cvx_unavailable()
                msg = "Failed to communicate with CVX master"
                raise arista_exc.AristaRpcError(msg=msg)
            return response
        except arista_exc.AristaRpcError:
            raise
    def __init__(self, rpc=None):

        self.ndb = db_lib.NeutronNets()
        self.db_nets = db.AristaProvisionedNets()
        self.db_vms = db.AristaProvisionedVms()
        self.db_tenants = db.AristaProvisionedTenants()

        confg = cfg.CONF.ml2_arista
        self.segmentation_type = db_lib.VLAN_SEGMENTATION
        self.timer = None
        self.sync_timeout = confg['sync_interval']
        self.managed_physnets = confg['managed_physnets']
        self.eos_sync_lock = threading.Lock()

        self.eapi = None

        if rpc:
            LOG.info("Using passed in parameter for RPC")
            self.rpc = rpc
            self.eapi = rpc
        else:
            self.eapi = arista_ml2.AristaRPCWrapperEapi(self.ndb)
            api_type = confg['api_type'].upper()
            if api_type == 'EAPI':
                LOG.info("Using EAPI for RPC")
                self.rpc = arista_ml2.AristaRPCWrapperEapi(self.ndb)
            elif api_type == 'JSON':
                LOG.info("Using JSON for RPC")
                self.rpc = arista_ml2.AristaRPCWrapperJSON(self.ndb)
            else:
                msg = "RPC mechanism %s not recognized" % api_type
                LOG.error(msg)
                raise arista_exc.AristaRpcError(msg=msg)

        self.sync_service = arista_ml2.SyncService(self.rpc, self.ndb)
        self.rpc.sync_service = self.sync_service
Ejemplo n.º 5
0
    def _send_eapi_req(self, cmds, commands_to_log=None):
        # This method handles all EAPI requests (using the requests library)
        # and returns either None or response.json()['result'] from the EAPI
        # request.
        #
        # Exceptions related to failures in connecting/ timeouts are caught
        # here and logged. Other unexpected exceptions are logged and raised

        request_headers = {}
        request_headers['Content-Type'] = 'application/json'
        request_headers['Accept'] = 'application/json'
        url = self._api_host_url(host=self._server_ip)

        params = {}
        params['timestamps'] = "false"
        params['format'] = "json"
        params['version'] = 1
        params['cmds'] = cmds

        data = {}
        data['id'] = "Arista ML2 driver"
        data['method'] = "runCmds"
        data['jsonrpc'] = "2.0"
        data['params'] = params

        response = None

        try:
            # NOTE(pbourke): shallow copy data and params to remove sensitive
            # information before logging
            log_data = dict(data)
            log_data['params'] = dict(params)
            log_data['params']['cmds'] = commands_to_log or cmds
            msg = (_('EAPI request to %(ip)s contains %(cmd)s') %
                   {'ip': self._server_ip, 'cmd': json.dumps(log_data)})
            LOG.info(msg)
            response = requests.post(url, timeout=self.conn_timeout,
                                     verify=False, data=json.dumps(data))
            LOG.info(_LI('EAPI response contains: %s'), response.json())
            try:
                return response.json()['result']
            except KeyError:
                if response.json()['error']['code'] == 1002:
                    for data in response.json()['error']['data']:
                        if type(data) == dict and 'errors' in data:
                            if const.ERR_CVX_NOT_LEADER in data['errors'][0]:
                                msg = six.text_type("%s is not the master" % (
                                                    self._server_ip))
                                LOG.info(msg)
                                return None

                msg = "Unexpected EAPI error"
                LOG.info(msg)
                raise arista_exc.AristaRpcError(msg=msg)
        except requests.exceptions.ConnectionError:
            msg = (_('Error while trying to connect to %(ip)s') %
                   {'ip': self._server_ip})
            LOG.warning(msg)
            return None
        except requests.exceptions.ConnectTimeout:
            msg = (_('Timed out while trying to connect to %(ip)s') %
                   {'ip': self._server_ip})
            LOG.warning(msg)
            return None
        except requests.exceptions.Timeout:
            msg = (_('Timed out during an EAPI request to %(ip)s') %
                   {'ip': self._server_ip})
            LOG.warning(msg)
            return None
        except requests.exceptions.InvalidURL:
            msg = (_('Ignore attempt to connect to invalid URL %(ip)s') %
                   {'ip': self._server_ip})
            LOG.warning(msg)
            return None
        except ValueError:
            LOG.info("Ignoring invalid JSON response")
            return None
        except Exception as error:
            msg = six.text_type(error)
            LOG.warning(msg)
            raise
Ejemplo n.º 6
0
    def execute(self, commands, commands_to_log=None):
        params = {
            'timestamps': False,
            'format': 'json',
            'version': 1,
            'cmds': commands
        }

        data = {
            'id': 'Networking Arista Driver',
            'method': 'runCmds',
            'jsonrpc': '2.0',
            'params': params
        }

        if commands_to_log:
            log_data = dict(data)
            log_data['params'] = dict(params)
            log_data['params']['cmds'] = commands_to_log
        else:
            log_data = data

        LOG.info(
            _LI('EAPI request %(ip)s contains %(data)s'),
            {'ip': self.host, 'data': json.dumps(log_data)}
        )

        # request handling
        try:
            error = None
            response = self.session.post(
                self.url,
                data=json.dumps(data),
                timeout=self.timeout
            )
        except requests_exc.ConnectionError:
            error = _LW('Error while trying to connect to %(ip)s')
        except requests_exc.ConnectTimeout:
            error = _LW('Timed out while trying to connect to %(ip)s')
        except requests_exc.Timeout:
            error = _LW('Timed out during an EAPI request to %(ip)s')
        except requests_exc.InvalidURL:
            error = _LW('Ingoring attempt to connect to invalid URL at %(ip)s')
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.warning(
                    _LW('Error during processing the EAPI request %(error)s'),
                    {'error': e}
                )
        finally:
            if error:
                msg = error % {'ip': self.host}
                # stop processing since we've encountered request error
                LOG.warning(msg)
                raise arista_exc.AristaRpcError(msg=msg)

        if response.status_code != requests.status_codes.codes.OK:
            msg = _LC(
                'Error (%(code)s - %(reason)s) while executing the command')
            LOG.error(msg, {
                'code': response.status_code,
                'reason': response.text})

        # response handling
        try:
            resp_data = response.json()
            return resp_data['result']
        except ValueError as e:
            LOG.info(_LI('Ignoring invalid JSON response'))
        except KeyError:
            if 'error' in resp_data and resp_data['error']['code'] == 1002:
                for d in resp_data['error']['data']:
                    if not isinstance(d, dict):
                        continue
                    elif ERR_CVX_NOT_LEADER in d.get('errors', {})[0]:
                        LOG.info(
                            _LI('%(ip)s is not the CVX leader'),
                            {'ip': self.host}
                        )
                        return
            msg = _LI('Unexpected EAPI error')
            LOG.info(msg)
            raise arista_exc.AristaRpcError(msg=msg)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.warning(
                    _LW('Error during processing the EAPI response %(error)s'),
                    {'error': e}
                )
Ejemplo n.º 7
0
    def _send_request(self, host, path, method, data=None,
                      sanitized_data=None):
        request_headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-Sync-ID': self.current_sync_name
        }
        url = self._api_host_url(host=host) + path
        # Don't log the password
        log_url = self._get_url(host=host, user=self._api_username(),
                                password="******") + path

        resp = None
        data = json.dumps(data)
        try:
            msg = (_('JSON request type: %(type)s url %(url)s data: '
                     '%(data)s sync_id: %(sync)s') %
                   {'type': method, 'url': log_url,
                    'data': sanitized_data or data,
                    'sync': self.current_sync_name})
            LOG.info(msg)
            func_lookup = {
                'GET': requests.get,
                'POST': requests.post,
                'PUT': requests.put,
                'PATCH': requests.patch,
                'DELETE': requests.delete
            }
            func = func_lookup.get(method)
            if not func:
                LOG.warning(_LW('Unrecognized HTTP method %s'), method)
                return None

            resp = func(url, timeout=self.conn_timeout, verify=False,
                        data=data, headers=request_headers)
            msg = (_LI('JSON response contains: %(code)s %(resp)s') %
                   {'code': resp.status_code,
                   'resp': resp.json()})
            LOG.info(msg)
            if resp.ok:
                return resp.json()
            else:
                raise arista_exc.AristaRpcError(msg=resp.json().get('error'))
        except requests.exceptions.ConnectionError:
            msg = (_('Error connecting to %(url)s') % {'url': url})
            LOG.warning(msg)
        except requests.exceptions.ConnectTimeout:
            msg = (_('Timed out connecting to API request to %(url)s') %
                   {'url': url})
            LOG.warning(msg)
        except requests.exceptions.Timeout:
            msg = (_('Timed out during API request to %(url)s') %
                   {'url': url})
            LOG.warning(msg)
        except requests.exceptions.InvalidURL:
            msg = (_('Ignore attempt to connect to invalid URL %(url)s') %
                   {'url': self._server_ip})
            LOG.warning(msg)
        except ValueError:
            LOG.warning(_LW("Ignoring invalid JSON response: %s"), resp.text)
        except Exception as error:
            msg = six.text_type(error)
            LOG.warning(msg)
            # reraise the exception
            with excutils.save_and_reraise_exception() as ctxt:
                ctxt.reraise = True
        return {} if method == 'GET' else None