diff --git a/lib/ansible/module_utils/network.py b/lib/ansible/module_utils/network.py index e6b54f3f9a7..c16c6895dfb 100644 --- a/lib/ansible/module_utils/network.py +++ b/lib/ansible/module_utils/network.py @@ -239,7 +239,7 @@ class NetCli(object): self._connected = False self.default_output = 'text' - def connect(self, params, kickstart, **kwargs): + def connect(self, params, kickstart=True, **kwargs): host = params['host'] port = params.get('port') or 22 diff --git a/lib/ansible/module_utils/openswitch.py b/lib/ansible/module_utils/openswitch.py index 097b9f11560..c296efe2ed2 100644 --- a/lib/ansible/module_utils/openswitch.py +++ b/lib/ansible/module_utils/openswitch.py @@ -18,8 +18,6 @@ # import re -import time -import json try: import ovs.poller @@ -30,32 +28,16 @@ try: except ImportError: HAS_OPS = False -from ansible.module_utils.basic import AnsibleModule, env_fallback, get_exception -from ansible.module_utils.shell import Shell, ShellError, HAS_PARAMIKO -from ansible.module_utils.netcfg import parse -from ansible.module_utils.urls import fetch_url - -NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) - -NET_COMMON_ARGS = dict( - host=dict(required=True), - port=dict(type='int'), - username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - password=dict(no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD'])), - ssh_keyfile=dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'), - validate_certs=dict(default=True, type='bool'), - transport=dict(default='ssh', choices=['ssh', 'cli', 'rest']), - use_ssl=dict(default=True, type='bool'), - provider=dict(type='dict') -) - -def to_list(val): - if isinstance(val, (list, tuple)): - return list(val) - elif val is not None: - return [val] - else: - return list() +from ansible.module_utils.basic import json, json_dict_bytes_to_unicode +from ansible.module_utils.network import ModuleStub, NetCli, NetworkError +from ansible.module_utils.network import add_argument, register_transport, to_list +from ansible.module_utils.urls import fetch_url, url_argument_spec + +# temporary fix until modules are update. to be removed before 2.2 final +from ansible.module_utils.network import get_module + +add_argument('use_ssl', dict(default=True, type='bool')) +add_argument('validate_certs', dict(default=True, type='bool')) def get_opsidl(): extschema = restparser.parseSchema(settings.get('ext_schema')) @@ -93,19 +75,25 @@ class Response(object): return None class Rest(object): + def __init__(self): + self.url = None + self.url_args = ModuleStub(url_argument_spec(), self._error) + self.headers = dict({'Content-Type': 'application/json', 'Accept': 'application/json'}) + self._connected = False - def __init__(self, module): - self.module = module - self.baseurl = None + def _error(self, msg): + raise NetworkError(msg, url=self.url) - def connect(self): - host = self.module.params['host'] - port = self.module.params['port'] + def connect(self, params, **kwargs): + host = params['host'] + port = params['port'] - self.module.params['url_username'] = self.module.params['username'] - self.module.params['url_password'] = self.module.params['password'] + # sets the module_utils/urls.py req parameters + self.url_args.params['url_username'] = params['username'] + self.url_args.params['url_password'] = params['password'] + self.url_args.params['validate_certs'] = params['validate_certs'] - if self.module.params['use_ssl']: + if params['use_ssl']: proto = 'https' if not port: port = 443 @@ -118,165 +106,119 @@ class Rest(object): headers = dict({'Content-Type': 'application/x-www-form-urlencoded'}) # Get a cookie and save it the rest of the operations. url = '%s/%s' % (baseurl, 'login') - data = 'username=%s&password=%s' % (self.module.params['username'], - self.module.params['password']) - resp, hdrs = fetch_url(self.module, url, data=data, - headers=headers, method='POST') + data = 'username=%s&password=%s' % (params['username'], params['password']) + resp, hdrs = fetch_url(self.url_args, url, data=data, headers=headers, method='POST') # Update the base url for the rest of the operations. - self.baseurl = '%s/rest/v1' % (baseurl) - self.headers = dict({'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Cookie': resp.headers.get('Set-Cookie')}) + self.url = '%s/rest/v1' % (baseurl) + self.headers['Cookie'] = resp.headers.get('Set-Cookie') + + def disconnect(self, **kwargs): + self.url = None + self._connected = False + + def authorize(self, params, **kwargs): + raise NotImplementedError def _url_builder(self, path): if path[0] == '/': path = path[1:] - return '%s/%s' % (self.baseurl, path) + return '%s/%s' % (self.url, path) - def send(self, method, path, data=None, headers=None): + def request(self, method, path, data=None, headers=None): url = self._url_builder(path) - data = self.module.jsonify(data) + data = self._jsonify(data) if headers is None: headers = dict() headers.update(self.headers) - resp, hdrs = fetch_url(self.module, url, data=data, headers=headers, - method=method) + resp, hdrs = fetch_url(self.url_args, url, data=data, headers=headers, method=method) return Response(resp, hdrs) def get(self, path, data=None, headers=None): - return self.send('GET', path, data, headers) + return self.request('GET', path, data, headers) def put(self, path, data=None, headers=None): - return self.send('PUT', path, data, headers) + return self.request('PUT', path, data, headers) def post(self, path, data=None, headers=None): - return self.send('POST', path, data, headers) + return self.request('POST', path, data, headers) def delete(self, path, data=None, headers=None): - return self.send('DELETE', path, data, headers) - -class Cli(object): - - def __init__(self, module): - self.module = module - self.shell = None - - def connect(self, **kwargs): - host = self.module.params['host'] - port = self.module.params['port'] or 22 - - username = self.module.params['username'] - password = self.module.params['password'] - key_filename = self.module.params['ssh_keyfile'] - - try: - self.shell = Shell() - self.shell.open(host, port=port, username=username, password=password, key_filename=key_filename) - except ShellError: - e = get_exception() - msg = 'failed to connect to %s:%s - %s' % (host, port, str(e)) - self.module.fail_json(msg=msg) - - def send(self, commands, encoding='text'): - try: - return self.shell.send(commands) - except ShellError: - e = get_exception() - self.module.fail_json(msg=e.message, commands=commands) + return self.request('DELETE', path, data, headers) + def configure(self, commands): + path = '/system/full-configuration' + return self.put(path, data=commands) -class NetworkModule(AnsibleModule): + def load_config(self, commands, **kwargs): + raise NotImplementedError - def __init__(self, *args, **kwargs): - super(NetworkModule, self).__init__(*args, **kwargs) - self.connection = None - self._config = None - self._opsidl = None - self._extschema = None + def get_config(self, **kwargs): + resp = self.get('/system/full-configuration') + return resp.json - @property - def config(self): - if not self._config: - self._config = self.get_config() - return self._config - - def _load_params(self): - super(NetworkModule, self)._load_params() - provider = self.params.get('provider') or dict() - for key, value in provider.items(): - if key in NET_COMMON_ARGS: - if self.params.get(key) is None and value is not None: - self.params[key] = value - - def connect(self): - cls = globals().get(str(self.params['transport']).capitalize()) - try: - self.connection = cls(self) - except TypeError: - e = get_exception() - self.fail_json(msg=e.message) + def commit_config(self, **kwargs): + raise NotImplementedError - self.connection.connect() + def abort_config(self, **kwargs): + raise NotImplementedError - def configure(self, commands): - if self.params['transport'] == 'cli': - commands = to_list(commands) - commands.insert(0, 'configure terminal') - responses = self.execute(commands) - responses.pop(0) - return responses - elif self.params['transport'] == 'rest': - path = '/system/full-configuration' - return self.connection.put(path, data=commands) - else: - if not self._opsidl: - (self._extschema, self._opsidl) = get_opsidl() - ops.dc.write(commands, self._extschema, self._opsidl) + def save_config(self): + raise NotImplementedError - def execute(self, commands, **kwargs): - return self.connection.send(commands, **kwargs) + def _jsonify(self, data): + for encoding in ("utf-8", "latin-1"): + try: + return json.dumps(data, encoding=encoding) + # Old systems using old simplejson module does not support encoding keyword. + except TypeError: + try: + new_data = json_dict_bytes_to_unicode(data, encoding=encoding) + except UnicodeDecodeError: + continue + return json.dumps(new_data) + except UnicodeDecodeError: + continue + self._error(msg='Invalid unicode encoding encountered') +Rest = register_transport('rest')(Rest) - def disconnect(self): - self.connection.close() - def parse_config(self, cfg): - return parse(cfg, indent=4) +class Cli(NetCli): + CLI_PROMPTS_RE = None - def get_config(self): - if self.params['transport'] == 'cli': - return self.execute('show running-config')[0] + CLI_ERRORS_RE = None - elif self.params['transport'] == 'rest': - resp = self.connection.get('/system/full-configuration') - return resp.json + NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I) + def configure(self, commands, **kwargs): + cmds = ['configure terminal'] + cmds.extend(to_list(commands)) - else: - if not self._opsidl: - (self._extschema, self._opsidl) = get_opsidl() - return ops.dc.read(self._extschema, self._opsidl) + responses = self.execute(cmds) + return responses[1:] + def get_config(self, params, **kwargs): + return self.execute('show running-config')[0] -def get_module(**kwargs): - """Return instance of NetworkModule - """ - argument_spec = NET_COMMON_ARGS.copy() - if kwargs.get('argument_spec'): - argument_spec.update(kwargs['argument_spec']) - kwargs['argument_spec'] = argument_spec + def load_config(self, commands, commit=False, **kwargs): + raise NotImplementedError - module = NetworkModule(**kwargs) + def replace_config(self, commands, **kwargs): + raise NotImplementedError - if not HAS_OPS and module.params['transport'] == 'ssh': - module.fail_json(msg='could not import ops library') + def commit_config(self, **kwargs): + raise NotImplementedError - if module.params['transport'] == 'cli' and not HAS_PARAMIKO: - module.fail_json(msg='paramiko is required but does not appear to be installed') + def abort_config(self, **kwargs): + raise NotImplementedError - if module.params['transport'] in ['cli', 'rest']: - module.connect() + def save_config(self): + raise NotImplementedError - return module + def run_commands(self, commands): + cmds = to_list(commands) + responses = self.execute(cmds) + return responses +Cli = register_transport('cli', default=True)(Cli)