library: merge branch 'th/ansible-independent'

Refactor the code, to make Cmd independent of ansible includes.
This would simplify to extend the library to run as stand-alone
application or to re-use parts without ansible.

Also, it better separates the code.

https://github.com/linux-system-roles/network/pull/23
This commit is contained in:
Thomas Haller 2018-01-22 11:01:58 +01:00
commit f62dea9c10
2 changed files with 406 additions and 308 deletions

View file

@ -1372,15 +1372,16 @@ class IfcfgUtil:
ifcfg[val[0]] = val[1]
return ifcfg
ANSIBLE_MANAGED = '# this file was created by ansible'
@classmethod
def content_from_dict(cls, ifcfg_all, file_type = None):
def content_from_dict(cls, ifcfg_all, file_type = None, header = None):
content = {}
for file_type in cls._file_types(file_type):
h = ifcfg_all[file_type]
if file_type == 'ifcfg':
s = cls.ANSIBLE_MANAGED + '\n'
if header is not None:
s = header + '\n'
else:
s = ""
for key in sorted(h.keys()):
value = h[key]
if not cls.KeyValid(key):
@ -1960,7 +1961,44 @@ class NMUtil:
###############################################################################
class _AnsibleUtil:
class RunEnvironment:
def __init__(self):
self._check_mode = None
@property
def ifcfg_header(self):
return None
def log(self,
connections,
idx,
severity,
msg,
is_changed = False,
ignore_errors = False,
warn_traceback = False,
force_fail = False):
raise NotImplementedError()
def run_command(self, argv, encoding = None):
raise NotImplementedError()
def _check_mode_changed(self, old_check_mode, new_check_mode, connections):
raise NotImplementedError()
def check_mode_set(self, check_mode, connections = None):
c = self._check_mode
self._check_mode = check_mode
assert( (c is None and check_mode in [CheckMode.PREPARE]) \
or (c == CheckMode.PREPARE and check_mode in [CheckMode.PRE_RUN, CheckMode.DRY_RUN]) \
or (c == CheckMode.PRE_RUN and check_mode in [CheckMode.REAL_RUN]) \
or (c == CheckMode.REAL_RUN and check_mode in [CheckMode.DONE]) \
or (c == CheckMode.DRY_RUN and check_mode in [CheckMode.DONE]))
self._check_mode_changed(c, check_mode, connections)
class RunEnvironmentAnsible(RunEnvironment):
ARGS = {
'ignore_errors': { 'required': False, 'default': False, 'type': 'str' },
@ -1969,142 +2007,175 @@ class _AnsibleUtil:
'connections': { 'required': False, 'default': None, 'type': 'list' },
}
ARGS_CONNECTIONS = ArgValidator_ListConnections()
def __init__(self):
self._module = None
self._connections = None
self._run_results = None
self._run_results_prepare = None
self._check_mode = CheckMode.PREPARE
RunEnvironment.__init__(self)
self._run_results = []
self._log_idx = 0
@property
def check_mode(self):
return self._check_mode
def check_mode_next(self):
if self._check_mode == CheckMode.PREPARE:
self._run_results_prepare = self._run_results
self._run_results = None
if self.module.check_mode:
self._check_mode = CheckMode.DRY_RUN
else:
self._check_mode = CheckMode.PRE_RUN
return self._check_mode
if self.check_mode == CheckMode.PRE_RUN:
self._run_results = None
self._check_mode = CheckMode.REAL_RUN
return CheckMode.REAL_RUN
if self._check_mode != CheckMode.DONE:
self._check_mode = CheckMode.DONE
return CheckMode.DONE
assert False
from ansible.module_utils.basic import AnsibleModule
module = AnsibleModule(
argument_spec = self.ARGS,
supports_check_mode = True,
)
self.module = module
@property
def module(self):
module = self._module
if module is None:
from ansible.module_utils.basic import AnsibleModule
module = AnsibleModule(
argument_spec = self.ARGS,
supports_check_mode = True,
)
self._module = module
return module
def ifcfg_header(self):
return '# this file was created by ansible'
def run_command(self, argv, encoding = None):
return self.module.run_command(argv, encoding = encoding)
def _run_results_push(self, n_connections):
c = []
for cc in range(0, n_connections + 1):
c.append({
'log': [],
})
self._run_results.append(c)
@property
def params(self):
return self.module.params
def run_results(self):
return self._run_results[-1]
def params_ignore_errors(self, connection, default_value = None):
v = connection['ignore_errors']
if v is None:
try:
v = Util.boolean(self.params['ignore_errors'])
except:
v = default_value
return v
def _check_mode_changed(self, old_check_mode, new_check_mode, connections):
if old_check_mode is None:
self._run_results_push(len(connections))
elif old_check_mode == CheckMode.PREPARE:
self._run_results_push(len(self.run_results) - 1)
elif old_check_mode == CheckMode.PRE_RUN:
# when switching from RRE_RUN to REAL_RUN, we drop the run-results
# we just collected and reset to empty. The PRE_RUN succeeded.
n_connections = len(self.run_results) - 1
del self._run_results[-1]
self._run_results_push(n_connections)
def params_force_state_change(self, connection, default_value = None):
v = connection['force_state_change']
if v is None:
if 'force_state_change' in self.params:
v = Util.boolean(self.params['force_state_change'])
if v is None:
v = default_value
return v
def log(self,
connections,
idx,
severity,
msg,
is_changed = False,
ignore_errors = False,
warn_traceback = False,
force_fail = False):
assert(idx >= -1)
self._log_idx += 1
self.run_results[idx]['log'].append((severity, msg, self._log_idx))
if severity == LogLevel.ERROR:
if force_fail \
or not ignore_errors:
self.fail_json(connections, 'error: %s' % (msg), changed = is_changed, warn_traceback = warn_traceback)
def _complete_kwargs_loglines(self, rr, connections, idx):
if idx == len(connections):
prefix = '#'
else:
c = connections[idx]
prefix = '#%s, state:%s' % (idx, c['state'])
if c['state'] != 'wait':
prefix = prefix + (', "%s"' % (c['name']))
for r in rr['log']:
yield (r[2], '[%03d] %s %s: %s' % (r[2], LogLevel.fmt(r[0]), prefix, r[1]))
def _complete_kwargs(self, connections, kwargs, traceback_msg = None):
if 'warnings' in kwargs:
logs = list(kwargs['warnings'])
else:
logs = []
l = []
for res in self._run_results:
for idx, rr in enumerate(res):
l.extend(self._complete_kwargs_loglines(rr, connections, idx))
l.sort(key = lambda x: x[0])
logs.extend([x[1] for x in l])
if traceback_msg is not None:
logs.append(traceback_msg)
kwargs['warnings'] = logs
return kwargs
def exit_json(self, connections, changed = False, **kwargs):
kwargs['changed'] = changed
self.module.exit_json(**self._complete_kwargs(connections, kwargs))
def fail_json(self, connections, msg, changed = False, warn_traceback = False, **kwargs):
traceback_msg = None
if warn_traceback:
traceback_msg = 'exception: %s' % (traceback.format_exc())
kwargs['msg'] = msg
kwargs['changed'] = changed
self.module.fail_json(**self._complete_kwargs(connections, kwargs, traceback_msg))
###############################################################################
class Cmd:
def __init__(self,
run_env,
connections_unvalidated,
connection_validator,
is_check_mode = False,
ignore_errors = False,
force_state_change = False):
self.run_env = run_env
self._connections_unvalidated = connections_unvalidated
self._connection_validator = connection_validator
self._is_check_mode = is_check_mode
self._ignore_errors = Util.boolean(ignore_errors)
self._force_state_change = Util.boolean(force_state_change)
self._connections = None
self._connections_data = None
self._check_mode = CheckMode.PREPARE
self._is_changed_modified_system = False
def run_command(argv, encoding = None):
return self.run_env.run_command(argv, encoding = encoding)
@property
def is_changed_modified_system(self):
return self._is_changed_modified_system
@property
def connections(self):
c = self._connections
if c is None:
try:
c = self.ARGS_CONNECTIONS.validate(self.params['connections'])
c = self._connection_validator.validate(self._connections_unvalidated)
except ValidationError as e:
self.fail_json('configuration error: %s' % (e),
warn_traceback = False)
raise MyError('configuration error: %s' % (e))
self._connections = c
return c
def connection_modified_earlier(self, idx):
# for index @idx, check if any of the previous profiles [0..idx[
# modify the connection.
con = self.connections[idx]
assert(con['state'] in ['up', 'down'])
# also check, if the current profile is 'up' with a 'type' (which
# possibly modifies the connection as well)
if con['state'] == 'up' \
and 'type' in con \
and self.run_results[idx]['changed']:
return True
for i in reversed(range(idx)):
c = self.connections[i]
if 'name' not in c:
continue
if c['name'] != con['name']:
continue
c_state = c['state']
if c_state == 'up' and 'type' not in c:
pass
elif c_state == 'down':
return True
elif c_state == 'absent':
return True
elif c_state in ['present', 'up']:
if self.run_results[i]['changed']:
return True
return False
@property
def run_results(self):
c = self._run_results
def connections_data(self):
c = self._connections_data
if c is None:
assert(self.check_mode in [CheckMode.DRY_RUN, CheckMode.PRE_RUN, CheckMode.REAL_RUN])
c = []
for cc in range(0, len(self.connections) + 1):
for idx in range(0, len(self.connections)):
c.append({
'changed': False,
'log': [],
'rc': [],
})
self._run_results = c
self._connections_data = c
return c
def run_results_changed(self, idx, changed = None):
assert(idx >= 0 and idx < len(self.run_results) - 1)
if changed is None:
changed = True
self.run_results[idx]['changed'] = bool(changed)
def connections_data_reset(self):
for c in self.connections_data:
c['changed'] = False
def run_results_rc(self, idx, rc, msg):
assert(idx >= 0 and idx < len(self.run_results) - 1)
self.run_results[idx]['rc'].append((rc, msg))
self.log(idx, LogLevel.INFO, 'command: %s (rc=%s)' % (msg, rc))
def connections_data_set_changed(self, idx, changed = True):
assert(self._check_mode in [CheckMode.PRE_RUN, CheckMode.DRY_RUN, CheckMode.REAL_RUN])
if not changed:
return
self.connections_data[idx]['changed'] = changed
if changed \
and self._check_mode in [CheckMode.DRY_RUN, CheckMode.REAL_RUN]:
# we only do actual modifications during the REAL_RUN step.
# And as a special exception, during the DRY_RUN step, which
# is like REAL_RUN, except not not actually changing anything.
self._is_changed_modified_system = True
def log_debug(self, idx, msg):
self.log(idx, LogLevel.DEBUG, msg)
@ -2122,98 +2193,110 @@ class _AnsibleUtil:
self.log(idx, LogLevel.ERROR, msg, warn_traceback = warn_traceback, force_fail = True)
def log(self, idx, severity, msg, warn_traceback = False, force_fail = False):
self._log_idx += 1
if idx == -1:
idx = len(self.run_results) - 1
else:
assert(idx >= 0 and idx < len(self.run_results) - 1)
self.run_results[idx]['log'].append((severity, msg, self._log_idx))
if severity == LogLevel.ERROR:
if force_fail \
or not self.params_ignore_errors(self.connections[idx], False):
self.fail_json('error: %s' % (msg), warn_traceback = warn_traceback)
def _complete_kwargs_loglines(self, rr, idx):
if idx == len(self.connections):
prefix = '#'
else:
c = self.connections[idx]
prefix = '#%s, state:%s' % (idx, c['state'])
if c['state'] != 'wait':
prefix = prefix + (', "%s"' % (c['name']))
for r in rr['log']:
yield (r[2], '[%03d] %s %s: %s' % (r[2], LogLevel.fmt(r[0]), prefix, r[1]))
def _complete_kwargs(self, kwargs, traceback_msg = None):
if 'warnings' in kwargs:
logs = list(kwargs['warnings'])
else:
logs = []
l = []
if self._run_results_prepare is not None:
for idx, rr in enumerate(self._run_results_prepare):
l.extend(self._complete_kwargs_loglines(rr, idx))
if self._run_results is not None:
for idx, rr in enumerate(self._run_results):
l.extend(self._complete_kwargs_loglines(rr, idx))
l.sort(key = lambda x: x[0])
logs.extend([x[1] for x in l])
if traceback_msg is not None:
logs.append(traceback_msg)
kwargs['warnings'] = logs
return kwargs
def exit_json(self, **kwargs):
changed = False
if self._run_results is not None:
for rr in self.run_results:
if rr['changed']:
changed = True
kwargs['changed'] = changed
self.module.exit_json(**self._complete_kwargs(kwargs))
def fail_json(self, msg, warn_traceback = False, **kwargs):
traceback_msg = None
if warn_traceback:
traceback_msg = 'exception: %s' % (traceback.format_exc())
kwargs['msg'] = msg
self.module.fail_json(**self._complete_kwargs(kwargs, traceback_msg))
AnsibleUtil = _AnsibleUtil()
###############################################################################
class Cmd:
self.run_env.log(connections,
idx,
severity,
msg,
is_changed = self.is_changed_modified_system,
ignore_errors = self.connection_ignore_errors(connections[idx]),
warn_traceback = warn_traceback,
force_fail = force_fail)
@staticmethod
def create():
provider = AnsibleUtil.params['provider']
def create(provider, **kwargs):
if provider == 'nm':
return Cmd_nm()
return Cmd_nm(**kwargs)
elif provider == 'initscripts':
return Cmd_initscripts()
AnsibleUtil.fail_json('unsupported provider %s' % (provider))
return Cmd_initscripts(**kwargs)
raise MyError('unsupported provider %s' % (provider))
def connection_force_state_change(self, connection):
v = connection['force_state_change']
if v is not None:
return v
return self._force_state_change
def connection_ignore_errors(self, connection):
v = connection['ignore_errors']
if v is not None:
return v
return self._ignore_errors
def connection_modified_earlier(self, idx):
# for index @idx, check if any of the previous profiles [0..idx[
# modify the connection.
con = self.connections[idx]
assert(con['state'] in ['up', 'down'])
# also check, if the current profile is 'up' with a 'type' (which
# possibly modifies the connection as well)
if con['state'] == 'up' \
and 'type' in con \
and self.connections_data[idx]['changed']:
return True
for i in reversed(range(idx)):
c = self.connections[i]
if 'name' not in c:
continue
if c['name'] != con['name']:
continue
c_state = c['state']
if c_state == 'up' and 'type' not in c:
pass
elif c_state == 'down':
return True
elif c_state == 'absent':
return True
elif c_state in ['present', 'up']:
if self.connections_data[idx]['changed']:
return True
return False
@property
def check_mode(self):
return self._check_mode
def check_mode_next(self):
if self._check_mode == CheckMode.PREPARE:
if self._is_check_mode:
c = CheckMode.DRY_RUN
else:
c = CheckMode.PRE_RUN
elif self.check_mode == CheckMode.PRE_RUN:
self.connections_data_reset()
c = CheckMode.REAL_RUN
elif self._check_mode != CheckMode.DONE:
c = CheckMode.DONE
else:
assert False
self._check_mode = c
self.run_env.check_mode_set(c)
return c
def run(self):
for idx, connection in enumerate(AnsibleUtil.connections):
self.run_env.check_mode_set(CheckMode.PREPARE, self.connections)
for idx, connection in enumerate(self.connections):
try:
AnsibleUtil.ARGS_CONNECTIONS.validate_connection_one(self.validate_one_type,
AnsibleUtil.connections,
idx)
self._connection_validator.validate_connection_one(self.validate_one_type,
self.connections,
idx)
except ValidationError as e:
AnsibleUtil.log_fatal(idx, str(e))
self.log_fatal(idx, str(e))
self.run_prepare()
while AnsibleUtil.check_mode_next() != CheckMode.DONE:
for idx, connection in enumerate(AnsibleUtil.connections):
while self.check_mode_next() != CheckMode.DONE:
for idx, connection in enumerate(self.connections):
try:
state = connection['state']
if state == 'wait':
w = connection['wait']
if w is None:
w = 10
AnsibleUtil.log_info(idx, 'wait for %s seconds' % (w))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'wait for %s seconds' % (w))
if self.check_mode == CheckMode.REAL_RUN:
import time
time.sleep(w)
elif state == 'absent':
@ -2229,11 +2312,11 @@ class Cmd:
else:
assert False
except Exception as e:
AnsibleUtil.log_warn(idx, 'failure: %s [[%s]]' % (e, traceback.format_exc()))
self.log_warn(idx, 'failure: %s [[%s]]' % (e, traceback.format_exc()))
raise
def run_prepare(self):
for idx, connection in enumerate(AnsibleUtil.connections):
for idx, connection in enumerate(self.connections):
if 'type' in connection and connection['check_iface_exists']:
# when the profile is tied to a certain interface via 'interface_name' or 'mac',
# check that such an interface exists.
@ -2246,23 +2329,24 @@ class Cmd:
if connection['mac']:
li_mac = SysUtil.link_info_find(mac = connection['mac'])
if not li_mac:
AnsibleUtil.log_fatal(idx, 'profile specifies mac "%s" but no such interface exists' % (connection['mac']))
self.log_fatal(idx, 'profile specifies mac "%s" but no such interface exists' % (connection['mac']))
if connection['interface_name']:
li_ifname = SysUtil.link_info_find(ifname = connection['interface_name'])
if not li_ifname:
if connection['type'] == 'ethernet':
AnsibleUtil.log_fatal(idx, 'profile specifies interface_name "%s" but no such interface exists' % (connection['interface_name']))
self.log_fatal(idx, 'profile specifies interface_name "%s" but no such interface exists' % (connection['interface_name']))
elif connection['type'] == 'infiniband':
if connection['infiniband_p_key'] in [None, -1]:
AnsibleUtil.log_fatal(idx, 'profile specifies interface_name "%s" but no such infiniband interface exists' % (connection['interface_name']))
self.log_fatal(idx, 'profile specifies interface_name "%s" but no such infiniband interface exists' % (connection['interface_name']))
if li_mac and li_ifname and li_mac != li_ifname:
AnsibleUtil.log_fatal(idx, 'profile specifies interface_name "%s" and mac "%s" but no such interface exists' % (connection['interface_name'], connection['mac']))
self.log_fatal(idx, 'profile specifies interface_name "%s" and mac "%s" but no such interface exists' % (connection['interface_name'], connection['mac']))
###############################################################################
class Cmd_nm(Cmd):
def __init__(self):
def __init__(self, **kwargs):
Cmd.__init__(self, **kwargs)
self._nmutil = None
self.validate_one_type = ArgValidator_ListConnections.VALIDATE_ONE_MODE_NM
@ -2272,14 +2356,14 @@ class Cmd_nm(Cmd):
try:
nmclient = Util.NM().Client.new(None)
except Exception as e:
AnsibleUtil.fail_json('failure loading libnm library: %s' % (e))
raise MyError('failure loading libnm library: %s' % (e))
self._nmutil = NMUtil(nmclient)
return self._nmutil
def run_prepare(self):
Cmd.run_prepare(self)
names = {}
for connection in AnsibleUtil.connections:
for connection in self.connections:
if connection['state'] not in ['up', 'down', 'present', 'absent']:
continue
name = connection['name']
@ -2305,52 +2389,50 @@ class Cmd_nm(Cmd):
connection['nm.uuid'] = uuid
def run_state_absent(self, idx):
changed = False
seen = set()
name = AnsibleUtil.connections[idx]['name']
name = self.connections[idx]['name']
black_list_names = None
if not name:
name = None
black_list_names = ArgUtil.connection_get_non_absent_names(AnsibleUtil.connections)
black_list_names = ArgUtil.connection_get_non_absent_names(self.connections)
while True:
connections = self.nmutil.connection_list(name = name, black_list_names = black_list_names, black_list = seen)
if not connections:
break
c = connections[-1]
seen.add(c)
AnsibleUtil.run_results_changed(idx)
AnsibleUtil.log_info(idx, 'delete connection %s, %s' % (c.get_id(), c.get_uuid()))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'delete connection %s, %s' % (c.get_id(), c.get_uuid()))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
self.nmutil.connection_delete(c)
except MyError as e:
AnsibleUtil.log_error(idx, 'delete connection failed: %s' % (e))
self.log_error(idx, 'delete connection failed: %s' % (e))
if not seen:
AnsibleUtil.log_info(idx, 'no connection "%s"' % (name))
self.log_info(idx, 'no connection "%s"' % (name))
def run_state_present(self, idx):
connection = AnsibleUtil.connections[idx]
connection = self.connections[idx]
con_cur = Util.first(self.nmutil.connection_list(name = connection['name'], uuid = connection['nm.uuid']))
con_new = self.nmutil.connection_create(AnsibleUtil.connections, idx, con_cur)
changed = False
con_new = self.nmutil.connection_create(self.connections, idx, con_cur)
if con_cur is None:
AnsibleUtil.log_info(idx, 'add connection %s, %s' % (connection['name'], connection['nm.uuid']))
changed = True
try:
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'add connection %s, %s' % (connection['name'], connection['nm.uuid']))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
con_cur = self.nmutil.connection_add(con_new)
except MyError as e:
AnsibleUtil.log_error(idx, 'adding connection failed: %s' % (e))
except MyError as e:
self.log_error(idx, 'adding connection failed: %s' % (e))
elif not self.nmutil.connection_compare(con_cur, con_new, normalize_a = True):
changed = True
AnsibleUtil.log_info(idx, 'update connection %s, %s' % (con_cur.get_id(), con_cur.get_uuid()))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'update connection %s, %s' % (con_cur.get_id(), con_cur.get_uuid()))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
self.nmutil.connection_update(con_cur, con_new)
except MyError as e:
AnsibleUtil.log_error(idx, 'updating connection failed: %s' % (e))
self.log_error(idx, 'updating connection failed: %s' % (e))
else:
AnsibleUtil.log_info(idx, 'connection %s, %s already up to date' % (con_cur.get_id(), con_cur.get_uuid()))
self.log_info(idx, 'connection %s, %s already up to date' % (con_cur.get_id(), con_cur.get_uuid()))
seen = set()
if con_cur is not None:
@ -2361,47 +2443,47 @@ class Cmd_nm(Cmd):
if not connections:
break
c = connections[-1]
AnsibleUtil.log_info(idx, 'delete duplicate connection %s, %s' % (c.get_id(), c.get_uuid()))
changed = True
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'delete duplicate connection %s, %s' % (c.get_id(), c.get_uuid()))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
self.nmutil.connection_delete(c)
except MyError as e:
AnsibleUtil.log_error(idx, 'delete duplicate connection failed: %s' % (e))
self.log_error(idx, 'delete duplicate connection failed: %s' % (e))
seen.add(c)
AnsibleUtil.run_results_changed(idx, changed)
def run_state_up(self, idx):
connection = AnsibleUtil.connections[idx]
connection = self.connections[idx]
con = Util.first(self.nmutil.connection_list(name = connection['name'], uuid = connection['nm.uuid']))
if not con:
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
AnsibleUtil.log_error(idx, 'up connection %s, %s failed: no connection' % (connection['name'], connection['nm.uuid']))
if self.check_mode == CheckMode.REAL_RUN:
self.log_error(idx, 'up connection %s, %s failed: no connection' % (connection['name'], connection['nm.uuid']))
else:
AnsibleUtil.log_info(idx, 'up connection %s, %s' % (connection['name'], connection['nm.uuid']))
self.log_info(idx, 'up connection %s, %s' % (connection['name'], connection['nm.uuid']))
return
is_active = self.nmutil.connection_is_active(con)
is_modified = AnsibleUtil.connection_modified_earlier(idx)
force_state_change = AnsibleUtil.params_force_state_change(connection, False)
is_modified = self.connection_modified_earlier(idx)
force_state_change = self.connection_force_state_change(connection)
if is_active and not force_state_change and not is_modified:
AnsibleUtil.log_info(idx, 'up connection %s, %s skipped because already active' %
(con.get_id(), con.get_uuid()))
self.log_info(idx, 'up connection %s, %s skipped because already active' %
(con.get_id(), con.get_uuid()))
return
AnsibleUtil.log_info(idx, 'up connection %s, %s (%s)' %
(con.get_id(), con.get_uuid(),
'not-active' if not is_active else \
'is-modified' if is_modified else \
'force-state-change'))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'up connection %s, %s (%s)' %
(con.get_id(), con.get_uuid(),
'not-active' if not is_active else \
'is-modified' if is_modified else \
'force-state-change'))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
ac = self.nmutil.connection_activate (con)
except MyError as e:
AnsibleUtil.log_error(idx, 'up connection failed: %s' % (e))
self.log_error(idx, 'up connection failed: %s' % (e))
wait_time = connection['wait']
if wait_time is None:
@ -2410,12 +2492,10 @@ class Cmd_nm(Cmd):
try:
self.nmutil.connection_activate_wait(ac, wait_time)
except MyError as e:
AnsibleUtil.log_error(idx, 'up connection failed while waiting: %s' % (e))
AnsibleUtil.run_results_changed(idx)
self.log_error(idx, 'up connection failed while waiting: %s' % (e))
def run_state_down(self, idx):
connection = AnsibleUtil.connections[idx]
connection = self.connections[idx]
cons = self.nmutil.connection_list(name = connection['name'])
changed = False
@ -2425,14 +2505,15 @@ class Cmd_nm(Cmd):
ac = Util.first(self.nmutil.active_connection_list(connections = cons, black_list = seen))
if ac is None:
break
changed = True
seen.add(ac)
AnsibleUtil.log_info(idx, 'down connection %s: %s' % (connection['name'], ac.get_path()))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'down connection %s: %s' % (connection['name'], ac.get_path()))
changed = True
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
self.nmutil.active_connection_deactivate(ac)
except MyError as e:
AnsibleUtil.log_error(idx, 'down connection failed: %s' % (e))
self.log_error(idx, 'down connection failed: %s' % (e))
wait_time = connection['wait']
if wait_time is None:
@ -2441,39 +2522,38 @@ class Cmd_nm(Cmd):
try:
self.nmutil.active_connection_deactivate_wait(ac, wait_time)
except MyError as e:
AnsibleUtil.log_error(idx, 'down connection failed while waiting: %s' % (e))
self.log_error(idx, 'down connection failed while waiting: %s' % (e))
cons = self.nmutil.connection_list(name = connection['name'])
if not changed:
AnsibleUtil.log_info(idx, 'down connection %s failed: no connection' % (connection['name']))
AnsibleUtil.run_results_changed(idx, changed)
self.log_info(idx, 'down connection %s failed: no connection' % (connection['name']))
###############################################################################
class Cmd_initscripts(Cmd):
def __init__(self):
def __init__(self, **kwargs):
Cmd.__init__(self, **kwargs)
self.validate_one_type = ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS
def check_name(self, idx, name = None):
if name is None:
name = AnsibleUtil.connections[idx]['name']
name = self.connections[idx]['name']
try:
f = IfcfgUtil.ifcfg_path(name)
except MyError as e:
AnsibleUtil.log_error(idx, 'invalid name %s for connection' % (name))
self.log_error(idx, 'invalid name %s for connection' % (name))
return None
return f
def run_state_absent(self, idx):
changed = False
n = AnsibleUtil.connections[idx]['name']
n = self.connections[idx]['name']
name = n
if not name:
names = []
black_list_names = ArgUtil.connection_get_non_absent_names(AnsibleUtil.connections)
black_list_names = ArgUtil.connection_get_non_absent_names(self.connections)
for f in os.listdir('/etc/sysconfig/network-scripts'):
if not f.startswith('ifcfg-'):
continue
@ -2487,58 +2567,60 @@ class Cmd_initscripts(Cmd):
if not self.check_name(idx):
return
names = [name]
changed = False
for name in names:
for path in IfcfgUtil.ifcfg_paths(name):
if not os.path.isfile(path):
continue
changed = True
AnsibleUtil.log_info(idx, 'delete ifcfg-rh file "%s"' % (path))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.log_info(idx, 'delete ifcfg-rh file "%s"' % (path))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
os.unlink(path)
except Exception as e:
AnsibleUtil.log_error(idx, 'delete ifcfg-rh file "%s" failed: %s' % (path, e))
self.log_error(idx, 'delete ifcfg-rh file "%s" failed: %s' % (path, e))
if not changed:
AnsibleUtil.log_info(idx, 'delete ifcfg-rh files for %s (no files present)' % ('"'+n+'"' if n else '*'))
AnsibleUtil.run_results_changed(idx, changed)
self.log_info(idx, 'delete ifcfg-rh files for %s (no files present)' % ('"'+n+'"' if n else '*'))
def run_state_present(self, idx):
if not self.check_name(idx):
return
connection = AnsibleUtil.connections[idx]
connection = self.connections[idx]
name = connection['name']
old_content = IfcfgUtil.content_from_file(name)
ifcfg_all = IfcfgUtil.ifcfg_create(AnsibleUtil.connections, idx,
lambda msg: AnsibleUtil.log_warn(idx, msg),
ifcfg_all = IfcfgUtil.ifcfg_create(self.connections, idx,
lambda msg: self.log_warn(idx, msg),
old_content)
new_content = IfcfgUtil.content_from_dict(ifcfg_all)
new_content = IfcfgUtil.content_from_dict(ifcfg_all,
header = self.run_env.ifcfg_header)
if old_content == new_content:
AnsibleUtil.log_info(idx, 'ifcfg-rh profile "%s" already up to date' % (name))
self.log_info(idx, 'ifcfg-rh profile "%s" already up to date' % (name))
return
op = 'add' if (old_content['ifcfg'] is None) else 'update'
AnsibleUtil.log_info(idx, '%s ifcfg-rh profile "%s"' % (op, name))
self.log_info(idx, '%s ifcfg-rh profile "%s"' % (op, name))
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
try:
IfcfgUtil.content_to_file(name, new_content)
except MyError as e:
AnsibleUtil.log_error(idx, '%s ifcfg-rh profile "%s" failed: %s' % (op, name, e))
AnsibleUtil.run_results_changed(idx)
self.log_error(idx, '%s ifcfg-rh profile "%s" failed: %s' % (op, name, e))
def _run_state_updown(self, idx, do_up):
if not self.check_name(idx):
return
connection = AnsibleUtil.connections[idx]
connection = self.connections[idx]
name = connection['name']
if connection['wait'] is not None:
@ -2548,47 +2630,46 @@ class Cmd_initscripts(Cmd):
path = IfcfgUtil.ifcfg_path(name)
if not os.path.isfile(path):
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
AnsibleUtil.log_error(idx, 'ifcfg file "%s" does not exist' % (path))
if self.check_mode == CheckMode.REAL_RUN:
self.log_error(idx, 'ifcfg file "%s" does not exist' % (path))
else:
AnsibleUtil.log_info(idx, 'ifcfg file "%s" does not exist in check mode' % (path))
self.log_info(idx, 'ifcfg file "%s" does not exist in check mode' % (path))
return
is_active = IfcfgUtil.connection_seems_active(name)
is_modified = AnsibleUtil.connection_modified_earlier(idx)
force_state_change = AnsibleUtil.params_force_state_change(connection, False)
is_modified = self.connection_modified_earlier(idx)
force_state_change = self.connection_force_state_change(connection)
if do_up:
if is_active is True and not force_state_change and not is_modified:
AnsibleUtil.log_info(idx, 'up connection %s skipped because already active' %
(name))
self.log_info(idx, 'up connection %s skipped because already active' %
(name))
return
AnsibleUtil.log_info(idx, 'up connection %s (%s)' %
(name,
'not-active' if is_active is not True else \
'is-modified' if is_modified else \
'force-state-change'))
self.log_info(idx, 'up connection %s (%s)' %
(name,
'not-active' if is_active is not True else \
'is-modified' if is_modified else \
'force-state-change'))
cmd = 'ifup'
else:
if is_active is False and not force_state_change:
AnsibleUtil.log_info(idx, 'down connection %s skipped because not active' %
(name))
self.log_info(idx, 'down connection %s skipped because not active' %
(name))
return
AnsibleUtil.log_info(idx, 'up connection %s (%s)' %
(name,
'active' if is_active is not False else \
'force-state-change'))
self.log_info(idx, 'up connection %s (%s)' %
(name,
'active' if is_active is not False else \
'force-state-change'))
cmd = 'ifdown'
if AnsibleUtil.check_mode == CheckMode.REAL_RUN:
rc, out, err = AnsibleUtil.module.run_command([cmd, name], encoding=None)
AnsibleUtil.log_info(idx, 'call `%s %s`: rc=%d, out="%s", err="%s"' % (cmd, name, rc, out, err))
self.connections_data_set_changed(idx)
if self.check_mode == CheckMode.REAL_RUN:
rc, out, err = self.run_env.run_command([cmd, name])
self.log_info(idx, 'call `%s %s`: rc=%d, out="%s", err="%s"' % (cmd, name, rc, out, err))
if rc != 0:
AnsibleUtil.log_error(idx, 'call `%s %s` failed with exit status %d' % (cmd, name, rc))
AnsibleUtil.run_results_changed(idx)
self.log_error(idx, 'call `%s %s` failed with exit status %d' % (cmd, name, rc))
def run_state_up(self, idx):
@ -2600,9 +2681,24 @@ class Cmd_initscripts(Cmd):
###############################################################################
if __name__ == '__main__':
connections = None
cmd = None
run_env_ansible = RunEnvironmentAnsible()
try:
Cmd.create().run()
params = run_env_ansible.module.params
cmd = Cmd.create(params['provider'],
run_env = run_env_ansible,
connections_unvalidated = params['connections'],
connection_validator = ArgValidator_ListConnections(),
is_check_mode = run_env_ansible.module.check_mode,
ignore_errors = params['ignore_errors'],
force_state_change = params['force_state_change'])
connections = cmd.connections
cmd.run()
except Exception as e:
AnsibleUtil.fail_json('fatal error: %s' % (e),
warn_traceback = True)
AnsibleUtil.exit_json()
run_env_ansible.fail_json(connections,
'fatal error: %s' % (e),
changed = (cmd is not None and cmd.is_changed_modified_system),
warn_traceback = not isinstance(e, MyError))
run_env_ansible.exit_json(connections,
changed = (cmd is not None and cmd.is_changed_modified_system))

View file

@ -40,6 +40,8 @@ def pprint(msg, obj):
if nmutil is not None and isinstance(obj, NM.Connection):
obj.dump()
ARGS_CONNECTIONS = n.ArgValidator_ListConnections()
class TestValidator(unittest.TestCase):
def assertValidationError(self, v, value):
@ -62,12 +64,12 @@ class TestValidator(unittest.TestCase):
self.assertEqual(route_list_exp, route_list_new)
def do_connections_check_invalid(self, input_connections):
self.assertValidationError(n.AnsibleUtil.ARGS_CONNECTIONS, input_connections)
self.assertValidationError(ARGS_CONNECTIONS, input_connections)
def do_connections_validate_nm(self, input_connections, **kwargs):
if not nmutil:
return
connections = n.AnsibleUtil.ARGS_CONNECTIONS.validate(input_connections)
connections = ARGS_CONNECTIONS.validate(input_connections)
for connection in connections:
if 'type' in connection:
connection['nm.exists'] = False
@ -75,7 +77,7 @@ class TestValidator(unittest.TestCase):
mode = n.ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS
for idx, connection in enumerate(connections):
try:
n.AnsibleUtil.ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
except n.ValidationError as e:
continue
if 'type' in connection:
@ -104,10 +106,10 @@ class TestValidator(unittest.TestCase):
def do_connections_validate_ifcfg(self, input_connections, **kwargs):
mode = n.ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS
connections = n.AnsibleUtil.ARGS_CONNECTIONS.validate(input_connections)
connections = ARGS_CONNECTIONS.validate(input_connections)
for idx, connection in enumerate(connections):
try:
n.AnsibleUtil.ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
except n.ValidationError as e:
continue
if 'type' in connection:
@ -122,7 +124,7 @@ class TestValidator(unittest.TestCase):
def do_connections_validate(self, expected_connections, input_connections, **kwargs):
connections = n.AnsibleUtil.ARGS_CONNECTIONS.validate(input_connections)
connections = ARGS_CONNECTIONS.validate(input_connections)
self.assertEqual(expected_connections, connections)
self.do_connections_validate_nm(input_connections, **kwargs)
self.do_connections_validate_ifcfg(input_connections, **kwargs)