def parse_rpc_error(self, rpc_error): if self.check_rc: try: error_root = fromstring(rpc_error) root = Element('root') root.append(error_root) error_list = root.findall('.//nc:rpc-error', NS_MAP) if not error_list: raise ConnectionError(to_text(rpc_error, errors='surrogate_then_replace')) warnings = [] for error in error_list: message_ele = error.find('./nc:error-message', NS_MAP) if message_ele is None: message_ele = error.find('./nc:error-info', NS_MAP) message = message_ele.text if message_ele is not None else None severity = error.find('./nc:error-severity', NS_MAP).text if severity == 'warning' and self.ignore_warning and message is not None: warnings.append(message) else: raise ConnectionError(to_text(rpc_error, errors='surrogate_then_replace')) return warnings except XMLSyntaxError: raise ConnectionError(rpc_error)
def run_commands(self, commands, check_rc=True): if commands is None: raise ValueError("'commands' value is required") headers = {'Content-Type': 'application/json'} responses = list() for cmd in to_list(commands): if not isinstance(cmd, Mapping): cmd = {'command': cmd} cmd['command'] = strip_run_script_cli2json(cmd['command']) output = cmd.pop('output', None) if output and output not in self.get_option_values().get('output'): raise ValueError( "'output' value is %s is invalid. Valid values are %s" % (output, ','.join(self.get_option_values().get('output')))) data = request_builder(cmd['command']) response, response_data = self.connection.send( '/jsonrpc', data, cookies=self._auth_token, headers=headers, method='POST') try: response_data = json.loads(to_text(response_data.getvalue())) except ValueError: raise ConnectionError( 'Response was not valid JSON, got {0}'.format( to_text(response_data.getvalue()))) if response_data.get('error', None): raise ConnectionError("Request Error, got {0}".format( response_data['error'])) if not response_data.get('result', None): raise ConnectionError( "Request Error, got {0}".format(response_data)) response_data = response_data['result'] if output and output == 'text': statusOut = getKeyInResponse(response_data, 'status') cliOut = getKeyInResponse(response_data, 'CLIoutput') if statusOut == "ERROR": raise ConnectionError( "Command error({1}) for request {0}".format( cmd['command'], cliOut)) if cliOut is None: raise ValueError( "Response for request {0} doesn't have the CLIoutput field, got {1}" .format(cmd['command'], response_data)) response_data = cliOut responses.append(response_data) return responses
def login(self, username, password): def request_token_payload(username, password): return { 'grant_type': 'password', 'username': username, 'password': password } def refresh_token_payload(refresh_token): return { 'grant_type': 'refresh_token', 'refresh_token': refresh_token } if self.refresh_token: payload = refresh_token_payload(self.refresh_token) elif username and password: payload = request_token_payload(username, password) else: raise AnsibleConnectionFailure( 'Username and password are required for login in absence of refresh token' ) response = self._lookup_login_url(payload) try: self.refresh_token = response['refresh_token'] self.access_token = response['access_token'] self.connection._auth = { 'Authorization': 'Bearer %s' % self.access_token } except KeyError: raise ConnectionError( 'Server returned response without token info during connection authentication: %s' % response)
def run_commands(self, commands=None, check_rc=True): if commands is None: raise ValueError("'commands' value is required") responses = list() for cmd in to_list(commands): if not isinstance(cmd, Mapping): cmd = {'command': cmd} output = cmd.pop('output', None) if output: cmd['command'] = self._get_command_with_output(cmd['command'], output) try: out = self.send_command(**cmd) except AnsibleConnectionFailure as e: if check_rc is True: raise out = getattr(e, 'err', e) if out is not None: try: out = to_text(out, errors='surrogate_or_strict').strip() except UnicodeError: raise ConnectionError(message=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) try: out = json.loads(out) except ValueError: pass responses.append(out) return responses
def _lookup_login_url(self, payload): """ Try to find correct login URL and get api token using this URL. :param payload: Token request payload :type payload: dict :return: token generation response """ preconfigured_token_path = self._get_api_token_path() if preconfigured_token_path: token_paths = [preconfigured_token_path] else: token_paths = self._get_known_token_paths() for url in token_paths: try: response = self._send_login_request(payload, url) except ConnectionError as e: self.connection.queue_message( 'vvvv', 'REST:request to %s failed because of connection error: %s ' % (url, e)) # In the case of ConnectionError caused by HTTPError we should check response code. # Response code 400 returned in case of invalid credentials so we should stop attempts to log in and # inform the user. if hasattr(e, 'http_code') and e.http_code == 400: raise else: if not preconfigured_token_path: self._set_api_token_path(url) return response raise ConnectionError( INVALID_API_TOKEN_PATH_MSG if preconfigured_token_path else MISSING_API_TOKEN_PATH_MSG)
def edit_config(self, candidate=None, commit=True, replace=None, comment=None): resp = {} operations = self.get_device_operations() self.check_edit_config_capability(operations, candidate, commit, replace, comment) results = [] requests = [] if replace: device_info = self.get_device_info() if '9K' not in device_info.get('network_os_platform', ''): raise ConnectionError(message=u'replace is supported only on Nexus 9K devices') candidate = 'config replace {0}'.format(replace) if commit: self.send_command('configure terminal') for line in to_list(candidate): if not isinstance(line, Mapping): line = {'command': line} cmd = line['command'] if cmd != 'end': results.append(self.send_command(**line)) requests.append(cmd) self.send_command('end') else: raise ValueError('check mode is not supported') resp['request'] = requests resp['response'] = results return resp
def send_request(self, data, **message_kwargs): data = to_list(data) become = self._become if become: self.connection.queue_message('vvvv', 'firing event: on_become') data.insert(0, {"cmd": "enable", "input": self._become_pass}) output = message_kwargs.get('output', 'text') request = request_builder(data, output) headers = {'Content-Type': 'application/json-rpc'} response, response_data = self.connection.send('/command-api', request, headers=headers, method='POST') try: response_data = json.loads(to_text(response_data.getvalue())) except ValueError: raise ConnectionError( 'Response was not valid JSON, got {0}'.format( to_text(response_data.getvalue()))) results = handle_response(response_data) if become: results = results[1:] if len(results) == 1: results = results[0] return results
def handle_response(response, response_data): try: response_data = json.loads(response_data.read()) except ValueError: response_data = response_data.read() if isinstance(response, HTTPError): if response_data: if 'errors' in response_data: errors = response_data['errors']['error'] error_text = '\n'.join((error['error-message'] for error in errors)) else: error_text = response_data raise ConnectionError(error_text, code=response.code) raise ConnectionError(to_text(response), code=response.code) return response_data
def login(self, username, password): if username and password: payload = {} url = '/rest/com/vmware/cis/session' response, response_data = self.send_request(url, payload) else: raise AnsibleConnectionFailure( 'Username and password are required for login') if response == 404: raise ConnectionError(response_data) if not response_data.get('value'): raise ConnectionError( 'Server returned response without token info during connection authentication: %s' % response) self.connection._session_uid = "vmware-api-session-id:%s" % response_data[ 'value'] self.connection._token = response_data['value']
def api_spec(self): if self._api_spec is None: spec_path_url = self._get_api_spec_path() response = self.send_request(url_path=spec_path_url, http_method=HTTPMethod.GET) if response[ResponseParams.SUCCESS]: self._api_spec = FdmSwaggerParser().parse_spec( response[ResponseParams.RESPONSE]) else: raise ConnectionError( 'Failed to download API specification. Status code: %s. Response: %s' % (response[ResponseParams.STATUS_CODE], response[ResponseParams.RESPONSE])) return self._api_spec
def _send_service_request(self, path, error_msg_prefix, data=None, **kwargs): try: self._ignore_http_errors = True return self.connection.send(path, data, **kwargs) except HTTPError as e: # HttpApi connection does not read the error response from HTTPError, so we do it here and wrap it up in # ConnectionError, so the actual error message is displayed to the user. error_msg = self._response_to_json(to_text(e.read())) raise ConnectionError('%s: %s' % (error_msg_prefix, error_msg), http_code=e.code) finally: self._ignore_http_errors = False
def handle_response(response): results = [] if response['ins_api'].get('outputs'): for output in to_list(response['ins_api']['outputs']['output']): if output['code'] != '200': # Best effort messages: some API output keys may not exist on some platforms input_data = output.get('input', '') msg = output.get('msg', '') clierror = output.get('clierror', '') raise ConnectionError('%s: %s: %s' % (input_data, msg, clierror), code=output['code']) elif 'body' in output: result = output['body'] if isinstance(result, dict): result = json.dumps(result) results.append(result.strip()) return results
def send_request(self, path, data=None, method='GET', **message_kwargs): headers = {'Content-Type': 'application/json'} response, response_data = self.connection.send( path, data, method=method, cookies=self._auth_token, headers=headers, **message_kwargs) try: if response.status == 204: response_data = {} else: response_data = json.loads(to_text(response_data.getvalue())) except ValueError: raise ConnectionError( 'Response was not valid JSON, got {0}'.format( to_text(response_data.getvalue()))) return response_data
def handle_response(response): if 'error' in response: error = response['error'] error_text = [] for data in error['data']: error_text.extend(data.get('errors', [])) error_text = '\n'.join(error_text) or error['message'] raise ConnectionError(error_text, code=error['code']) results = [] for result in response['result']: if 'messages' in result: results.append(result['messages'][0]) elif 'output' in result: results.append(result['output'].strip()) else: results.append(json.dumps(result)) return results
def _run_queue(self, queue, output): if self._become: self.connection.queue_message('vvvv', 'firing event: on_become') queue.insert(0, 'enable') request = request_builder(queue, output) headers = {'Content-Type': 'application/json'} response, response_data = self.connection.send('/ins', request, headers=headers, method='POST') try: response_data = json.loads(to_text(response_data.getvalue())) except ValueError: raise ConnectionError('Response was not valid JSON, got {0}'.format( to_text(response_data.getvalue()) )) results = handle_response(response_data) if self._become: results = results[1:] return results
def request_builder(commands, output, version='1.0', chunk='0', sid=None): """Encodes a NXAPI JSON request message """ output_to_command_type = { 'text': 'cli_show_ascii', 'json': 'cli_show', 'bash': 'bash', 'config': 'cli_conf' } maybe_output = commands[0].split('|')[-1].strip() if maybe_output in output_to_command_type: command_type = output_to_command_type[maybe_output] commands = [command.split('|')[0].strip() for command in commands] else: try: command_type = output_to_command_type[output] except KeyError: msg = 'invalid format, received %s, expected one of %s' % \ (output, ','.join(output_to_command_type.keys())) raise ConnectionError(msg) if isinstance(commands, (list, set, tuple)): commands = ' ;'.join(commands) # Order should not matter but some versions of NX-OS software fail # to process the payload properly if 'input' gets serialized before # 'type' and the payload of 'input' contains the word 'type'. msg = collections.OrderedDict() msg['version'] = version msg['type'] = command_type msg['chunk'] = chunk msg['sid'] = sid msg['input'] = commands msg['output_format'] = 'json' return json.dumps(dict(ins_api=msg))
def login(self, username, password): if username and password: cp_domain = self.get_option('domain') if cp_domain: payload = { 'user': username, 'password': password, 'domain': cp_domain } else: payload = {'user': username, 'password': password} url = '/web_api/login' response, response_data = self.send_request(url, payload) else: raise AnsibleConnectionFailure( 'Username and password are required for login') try: self.connection._auth = {'X-chkp-sid': response_data['sid']} self.connection._session_uid = response_data['uid'] except KeyError: raise ConnectionError( 'Server returned response without token info during connection authentication: %s' % response)
def _response_to_json(self, response_text): try: return json.loads(response_text) if response_text else {} # JSONDecodeError only available on Python 3.5+ except ValueError: raise ConnectionError('Invalid JSON response: %s' % response_text)
def _response_to_json(response_text): try: return json.loads(response_text) if response_text else {} # JSONDecodeError only available on Python 3.5+ except getattr(json.decoder, 'JSONDecodeError', ValueError): raise ConnectionError('Invalid JSON response: %s' % response_text)