1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 07:51:50 +00:00

modules s[f-z]*: use f-strings (#10977)

* modules s[f-z]*: use f-strings

* add changelog frag
This commit is contained in:
Alexei Znamensky 2025-10-26 22:35:30 +13:00 committed by GitHub
parent 73452acf84
commit af246f8de3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 274 additions and 273 deletions

View file

@ -0,0 +1,29 @@
minor_changes:
- simpleinit_msb - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- slack - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- slackpkg - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- snap - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- snmp_facts - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- solaris_zone - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- sorcery - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- spectrum_device - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- spectrum_model_attrs - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- spotinst_aws_elastigroup - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- ss_3par_cpg - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- ssh_config - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- stacki_host - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- statsd - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- statusio_maintenance - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- sudoers - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- supervisorctl - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- svc - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- svr4pkg - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- swdepot - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- swupd - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- syslogger - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- syspatch - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- sysrc - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- systemd_creds_decrypt - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- systemd_creds_encrypt - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- systemd_info - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).
- sysupgrade - use f-strings for string templating (https://github.com/ansible-collections/community.general/pull/10977).

View file

@ -173,9 +173,9 @@ class SimpleinitMSB(object):
if not self.enable ^ self.service_enabled():
return
action = "boot" + ("enable" if self.enable else "disable")
action = f"boot{'enable' if self.enable else 'disable'}"
(rc, out, err) = self.execute_command("%s %s %s" % (self.telinit_cmd, action, self.name))
(rc, out, err) = self.execute_command(f"{self.telinit_cmd} {action} {self.name}")
self.changed = True
@ -195,11 +195,11 @@ class SimpleinitMSB(object):
def service_enabled(self):
self.service_exists()
(rc, out, err) = self.execute_command("%s %sd" % (self.telinit_cmd, self.enable))
(rc, out, err) = self.execute_command(f"{self.telinit_cmd} {self.enable}d")
service_enabled = False if self.enable else True
rex = re.compile(r'^%s$' % self.name)
rex = re.compile(rf'^{self.name}$')
for line in out.splitlines():
if rex.match(line):
@ -209,11 +209,11 @@ class SimpleinitMSB(object):
return service_enabled
def service_exists(self):
(rc, out, err) = self.execute_command("%s list" % self.telinit_cmd)
(rc, out, err) = self.execute_command(f"{self.telinit_cmd} list")
service_exists = False
rex = re.compile(r'^\w+\s+%s$' % self.name)
rex = re.compile(rf'^\w+\s+{self.name}$')
for line in out.splitlines():
if rex.match(line):
@ -221,14 +221,14 @@ class SimpleinitMSB(object):
break
if not service_exists:
self.module.fail_json(msg='telinit could not find the requested service: %s' % self.name)
self.module.fail_json(msg=f'telinit could not find the requested service: {self.name}')
def service_control(self):
self.service_exists()
svc_cmd = "%s run %s" % (self.telinit_cmd, self.name)
svc_cmd = f"{self.telinit_cmd} run {self.name}"
rc_state, stdout, stderr = self.execute_command("%s %s" % (svc_cmd, self.action), daemon=True)
rc_state, stdout, stderr = self.execute_command(f"{svc_cmd} {self.action}", daemon=True)
return (rc_state, stdout, stderr)

View file

@ -323,9 +323,9 @@ def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_e
if channel.startswith(('#', '@', 'C0', 'GF', 'G0', 'CP')):
payload['channel'] = channel
else:
payload['channel'] = '#' + channel
payload['channel'] = f"#{channel}"
elif prepend_hash == 'always':
payload['channel'] = '#' + channel
payload['channel'] = f"#{channel}"
elif prepend_hash == 'never':
payload['channel'] = channel
if thread_id is not None:
@ -383,7 +383,7 @@ def get_slack_message(module, domain, token, channel, ts):
headers = {
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
'Authorization': f"Bearer {token}"
}
qs = urlencode({
'channel': channel,
@ -392,17 +392,17 @@ def get_slack_message(module, domain, token, channel, ts):
'inclusive': 'true',
})
domain = validate_slack_domain(domain)
url = (SLACK_CONVERSATIONS_HISTORY_WEBAPI % domain) + '?' + qs
url = f"{SLACK_CONVERSATIONS_HISTORY_WEBAPI % domain}?{qs}"
response, info = fetch_url(module=module, url=url, headers=headers, method='GET')
if info['status'] != 200:
module.fail_json(msg="failed to get slack message")
data = module.from_json(response.read())
if data.get('ok') is False:
module.fail_json(msg="failed to get slack message: %s" % data)
module.fail_json(msg=f"failed to get slack message: {data}")
if len(data['messages']) < 1:
module.fail_json(msg="no messages matching ts: %s" % ts)
module.fail_json(msg=f"no messages matching ts: {ts}")
if len(data['messages']) > 1:
module.fail_json(msg="more than 1 message matching ts: %s" % ts)
module.fail_json(msg=f"more than 1 message matching ts: {ts}")
return data['messages'][0]
@ -427,7 +427,7 @@ def do_notify_slack(module, domain, token, payload):
'Accept': 'application/json',
}
if use_webapi:
headers['Authorization'] = 'Bearer ' + token
headers['Authorization'] = f"Bearer {token}"
data = module.jsonify(payload)
response, info = fetch_url(module=module, url=slack_uri, headers=headers, method='POST', data=data)
@ -437,7 +437,7 @@ def do_notify_slack(module, domain, token, payload):
obscured_incoming_webhook = slack_uri
else:
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % (domain, '[obscured]')
module.fail_json(msg=" failed to send %s to %s: %s" % (data, obscured_incoming_webhook, info['msg']))
module.fail_json(msg=f" failed to send {data} to {obscured_incoming_webhook}: {info['msg']}")
# each API requires different handling
if use_webapi:
@ -487,8 +487,7 @@ def main():
color_choices = ['normal', 'good', 'warning', 'danger']
if color not in color_choices and not is_valid_hex_color(color):
module.fail_json(msg="Color value specified should be either one of %r "
"or any valid hex value with length 3 or 6." % color_choices)
module.fail_json(msg=f"Color value specified should be either one of {color_choices} or any valid hex value with length 3 or 6.")
changed = True

View file

@ -83,7 +83,7 @@ def query_package(module, slackpkg_path, name):
# Exception for kernel-headers package on x86_64
if name == 'kernel-headers' and machine == 'x86_64':
machine = 'x86'
pattern = re.compile('^%s-[^-]+-(%s|noarch|fw)-[^-]+$' % (re.escape(name), re.escape(machine)))
pattern = re.compile(f'^{re.escape(name)}-[^-]+-({re.escape(machine)}|noarch|fw)-[^-]+$')
packages = [f for f in os.listdir('/var/log/packages') if pattern.match(f)]
if len(packages) > 0:
@ -107,13 +107,13 @@ def remove_packages(module, slackpkg_path, packages):
if not module.check_mode and query_package(module, slackpkg_path,
package):
module.fail_json(msg="failed to remove %s: %s" % (package, out))
module.fail_json(msg=f"failed to remove {package}: {out}")
remove_c += 1
if remove_c > 0:
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
module.exit_json(changed=True, msg=f"removed {remove_c} package(s)")
module.exit_json(changed=False, msg="package(s) already absent")
@ -132,14 +132,13 @@ def install_packages(module, slackpkg_path, packages):
if not module.check_mode and not query_package(module, slackpkg_path,
package):
module.fail_json(msg="failed to install %s: %s" % (package, out),
module.fail_json(msg=f"failed to install {package}: {out}",
stderr=err)
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="present %s package(s)"
% (install_c))
module.exit_json(changed=True, msg=f"present {install_c} package(s)")
module.exit_json(changed=False, msg="package(s) already present")
@ -154,14 +153,13 @@ def upgrade_packages(module, slackpkg_path, packages):
if not module.check_mode and not query_package(module, slackpkg_path,
package):
module.fail_json(msg="failed to install %s: %s" % (package, out),
module.fail_json(msg=f"failed to install {package}: {out}",
stderr=err)
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="present %s package(s)"
% (install_c))
module.exit_json(changed=True, msg=f"present {install_c} package(s)")
module.exit_json(changed=False, msg="package(s) already present")

View file

@ -277,7 +277,7 @@ class Snap(StateModuleHelper):
"The output format of 'snap set' may have changed. Aborting!")
for key, value in json_subtree.items():
full_key = key if prefix is None else prefix + "." + key
full_key = key if prefix is None else f"{prefix}.{key}"
if isinstance(value, (str, float, bool, numbers.Integral)):
option_map[full_key] = str(value)
@ -307,7 +307,7 @@ class Snap(StateModuleHelper):
return option_map
except Exception as e:
self.do_raise(
msg="Parsing option map returned by 'snap get {0}' triggers exception '{1}', output:\n'{2}'".format(snap_name, str(e), out))
msg=f"Parsing option map returned by 'snap get {snap_name}' triggers exception '{e}', output:\n'{out}'")
def names_from_snaps(self, snaps):
def process_one(rc, out, err):
@ -353,7 +353,7 @@ class Snap(StateModuleHelper):
match = [c for n, c in installed if n == name]
if not match:
return Snap.NOT_INSTALLED
if channel and match[0] not in (channel, "latest/{0}".format(channel)):
if channel and match[0] not in (channel, f"latest/{channel}"):
return Snap.CHANNEL_MISMATCH
else:
return Snap.INSTALLED
@ -376,7 +376,7 @@ class Snap(StateModuleHelper):
result = out.splitlines()[1]
match = self.__disable_re.match(result)
if not match:
self.do_raise(msg="Unable to parse 'snap list {0}' output:\n{1}".format(snap_name, out))
self.do_raise(msg=f"Unable to parse 'snap list {snap_name}' output:\n{out}")
notes = match.group('notes')
return "disabled" not in notes.split(',')
@ -405,10 +405,9 @@ class Snap(StateModuleHelper):
match = classic_snap_pattern.match(err)
if match:
err_pkg = match.group('package_name')
msg = "Couldn't install {name} because it requires classic confinement".format(name=err_pkg)
msg = f"Couldn't install {err_pkg} because it requires classic confinement"
else:
msg = "Ooops! Snap installation failed while executing '{cmd}', please examine logs and " \
"error output for more details.".format(cmd=self.vars.cmd)
msg = f"Ooops! Snap installation failed while executing '{self.vars.cmd}', please examine logs and error output for more details."
self.do_raise(msg=msg)
def state_present(self):
@ -441,14 +440,14 @@ class Snap(StateModuleHelper):
match = self.__set_param_re.match(option_string)
if not match:
msg = "Cannot parse set option '{option_string}'".format(option_string=option_string)
msg = f"Cannot parse set option '{option_string}'"
self.do_raise(msg)
snap_prefix = match.group("snap_prefix")
selected_snap_name = snap_prefix[:-1] if snap_prefix else None
if selected_snap_name is not None and selected_snap_name not in self.vars.name:
msg = "Snap option '{option_string}' refers to snap which is not in the list of snap names".format(option_string=option_string)
msg = f"Snap option '{option_string}' refers to snap which is not in the list of snap names"
self.do_raise(msg)
if selected_snap_name is None or (snap_name is not None and snap_name == selected_snap_name):
@ -456,8 +455,8 @@ class Snap(StateModuleHelper):
value = match.group("value").strip()
if key not in option_map or key in option_map and option_map[key] != value:
option_without_prefix = key + "=" + value
option_with_prefix = option_string if selected_snap_name is not None else snap_name + ":" + option_string
option_without_prefix = f"{key}={value}"
option_with_prefix = option_string if selected_snap_name is not None else f"{snap_name}:{option_string}"
options_changed.append(option_without_prefix)
overall_options_changed.append(option_with_prefix)
@ -469,11 +468,10 @@ class Snap(StateModuleHelper):
rc, out, err = ctx.run(name=snap_name, options=options_changed)
if rc != 0:
if 'has no "configure" hook' in err:
msg = "Snap '{snap}' does not have any configurable options".format(snap=snap_name)
msg = f"Snap '{snap_name}' does not have any configurable options"
self.do_raise(msg)
msg = "Cannot set options '{options}' for snap '{snap}': error={error}".format(
options=" ".join(options_changed), snap=snap_name, error=err)
msg = f"Cannot set options '{' '.join(options_changed)}' for snap '{snap_name}': error={err}"
self.do_raise(msg)
if overall_options_changed:
@ -491,8 +489,7 @@ class Snap(StateModuleHelper):
self.vars.run_info = run_info
if rc == 0:
return
msg = "Ooops! Snap operation failed while executing '{cmd}', please examine logs and " \
"error output for more details.".format(cmd=self.vars.cmd)
msg = f"Ooops! Snap operation failed while executing '{self.vars.cmd}', please examine logs and error output for more details."
self.do_raise(msg=msg)
def state_absent(self):

View file

@ -204,27 +204,27 @@ class DefineOid(object):
dp = ""
# From SNMPv2-MIB
self.sysDescr = dp + "1.3.6.1.2.1.1.1.0"
self.sysObjectId = dp + "1.3.6.1.2.1.1.2.0"
self.sysUpTime = dp + "1.3.6.1.2.1.1.3.0"
self.sysContact = dp + "1.3.6.1.2.1.1.4.0"
self.sysName = dp + "1.3.6.1.2.1.1.5.0"
self.sysLocation = dp + "1.3.6.1.2.1.1.6.0"
self.sysDescr = f"{dp}1.3.6.1.2.1.1.1.0"
self.sysObjectId = f"{dp}1.3.6.1.2.1.1.2.0"
self.sysUpTime = f"{dp}1.3.6.1.2.1.1.3.0"
self.sysContact = f"{dp}1.3.6.1.2.1.1.4.0"
self.sysName = f"{dp}1.3.6.1.2.1.1.5.0"
self.sysLocation = f"{dp}1.3.6.1.2.1.1.6.0"
# From IF-MIB
self.ifIndex = dp + "1.3.6.1.2.1.2.2.1.1"
self.ifDescr = dp + "1.3.6.1.2.1.2.2.1.2"
self.ifMtu = dp + "1.3.6.1.2.1.2.2.1.4"
self.ifSpeed = dp + "1.3.6.1.2.1.2.2.1.5"
self.ifPhysAddress = dp + "1.3.6.1.2.1.2.2.1.6"
self.ifAdminStatus = dp + "1.3.6.1.2.1.2.2.1.7"
self.ifOperStatus = dp + "1.3.6.1.2.1.2.2.1.8"
self.ifAlias = dp + "1.3.6.1.2.1.31.1.1.1.18"
self.ifIndex = f"{dp}1.3.6.1.2.1.2.2.1.1"
self.ifDescr = f"{dp}1.3.6.1.2.1.2.2.1.2"
self.ifMtu = f"{dp}1.3.6.1.2.1.2.2.1.4"
self.ifSpeed = f"{dp}1.3.6.1.2.1.2.2.1.5"
self.ifPhysAddress = f"{dp}1.3.6.1.2.1.2.2.1.6"
self.ifAdminStatus = f"{dp}1.3.6.1.2.1.2.2.1.7"
self.ifOperStatus = f"{dp}1.3.6.1.2.1.2.2.1.8"
self.ifAlias = f"{dp}1.3.6.1.2.1.31.1.1.1.18"
# From IP-MIB
self.ipAdEntAddr = dp + "1.3.6.1.2.1.4.20.1.1"
self.ipAdEntIfIndex = dp + "1.3.6.1.2.1.4.20.1.2"
self.ipAdEntNetMask = dp + "1.3.6.1.2.1.4.20.1.3"
self.ipAdEntAddr = f"{dp}1.3.6.1.2.1.4.20.1.1"
self.ipAdEntIfIndex = f"{dp}1.3.6.1.2.1.4.20.1.2"
self.ipAdEntNetMask = f"{dp}1.3.6.1.2.1.4.20.1.3"
def decode_hex(hexstring):

View file

@ -196,20 +196,20 @@ class Zone(object):
t = tempfile.NamedTemporaryFile(delete=False, mode='wt')
if self.sparse:
t.write('create %s\n' % self.create_options)
t.write(f'create {self.create_options}\n')
self.msg.append('creating sparse-root zone')
else:
t.write('create -b %s\n' % self.create_options)
t.write(f'create -b {self.create_options}\n')
self.msg.append('creating whole-root zone')
t.write('set zonepath=%s\n' % self.path)
t.write('%s\n' % self.config)
t.write(f'set zonepath={self.path}\n')
t.write(f'{self.config}\n')
t.close()
cmd = [self.zonecfg_cmd, '-z', self.name, '-f', t.name]
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to create zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to create zone. {out + err}')
os.unlink(t.name)
self.changed = True
@ -220,7 +220,7 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'install', self.install_options]
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to install zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to install zone. {out + err}')
if int(self.os_minor) == 10:
self.configure_sysid()
self.configure_password()
@ -234,20 +234,20 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'uninstall', '-F']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to uninstall zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to uninstall zone. {out + err}')
self.changed = True
self.msg.append('zone uninstalled')
def configure_sysid(self):
if os.path.isfile('%s/root/etc/.UNCONFIGURED' % self.path):
os.unlink('%s/root/etc/.UNCONFIGURED' % self.path)
if os.path.isfile(f'{self.path}/root/etc/.UNCONFIGURED'):
os.unlink(f'{self.path}/root/etc/.UNCONFIGURED')
open('%s/root/noautoshutdown' % self.path, 'w').close()
open(f'{self.path}/root/noautoshutdown', 'w').close()
with open('%s/root/etc/nodename' % self.path, 'w') as node:
with open(f'{self.path}/root/etc/nodename', 'w') as node:
node.write(self.name)
with open('%s/root/etc/.sysIDtool.state' % self.path, 'w') as id:
with open(f'{self.path}/root/etc/.sysIDtool.state', 'w') as id:
id.write('1 # System previously configured?\n')
id.write('1 # Bootparams succeeded?\n')
id.write('1 # System is on a network?\n')
@ -262,23 +262,23 @@ class Zone(object):
id.write('vt100')
def configure_ssh_keys(self):
rsa_key_file = '%s/root/etc/ssh/ssh_host_rsa_key' % self.path
dsa_key_file = '%s/root/etc/ssh/ssh_host_dsa_key' % self.path
rsa_key_file = f'{self.path}/root/etc/ssh/ssh_host_rsa_key'
dsa_key_file = f'{self.path}/root/etc/ssh/ssh_host_dsa_key'
if not os.path.isfile(rsa_key_file):
cmd = [self.ssh_keygen_cmd, '-f', rsa_key_file, '-t', 'rsa', '-N', '']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to create rsa key. %s' % (out + err))
self.module.fail_json(msg=f'Failed to create rsa key. {out + err}')
if not os.path.isfile(dsa_key_file):
cmd = [self.ssh_keygen_cmd, '-f', dsa_key_file, '-t', 'dsa', '-N', '']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to create dsa key. %s' % (out + err))
self.module.fail_json(msg=f'Failed to create dsa key. {out + err}')
def configure_password(self):
shadow = '%s/root/etc/shadow' % self.path
shadow = f'{self.path}/root/etc/shadow'
if self.root_password:
with open(shadow, 'r') as f:
lines = f.readlines()
@ -298,7 +298,7 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'boot']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to boot zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to boot zone. {out + err}')
"""
The boot command can return before the zone has fully booted. This is especially
@ -311,7 +311,7 @@ class Zone(object):
while True:
if elapsed > self.timeout:
self.module.fail_json(msg='timed out waiting for zone to boot')
rc = os.system('ps -z %s -o args|grep "ttymon.*-d /dev/console" > /dev/null 2>/dev/null' % self.name)
rc = os.system(f'ps -z {self.name} -o args|grep "ttymon.*-d /dev/console" > /dev/null 2>/dev/null')
if rc == 0:
break
time.sleep(10)
@ -328,7 +328,7 @@ class Zone(object):
cmd = [self.zonecfg_cmd, '-z', self.name, 'delete', '-F']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to delete zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to delete zone. {out + err}')
self.changed = True
self.msg.append('zone deleted')
@ -337,7 +337,7 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'halt']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to stop zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to stop zone. {out + err}')
self.changed = True
self.msg.append('zone stopped')
@ -346,7 +346,7 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'detach']
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to detach zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to detach zone. {out + err}')
self.changed = True
self.msg.append('zone detached')
@ -355,7 +355,7 @@ class Zone(object):
cmd = [self.zoneadm_cmd, '-z', self.name, 'attach', self.attach_options]
(rc, out, err) = self.module.run_command(cmd)
if rc != 0:
self.module.fail_json(msg='Failed to attach zone. %s' % (out + err))
self.module.fail_json(msg=f'Failed to attach zone. {out + err}')
self.changed = True
self.msg.append('zone attached')
@ -473,7 +473,7 @@ def main():
elif state == 'attached':
zone.state_attached()
else:
module.fail_json(msg='Invalid state: %s' % state)
module.fail_json(msg=f'Invalid state: {state}')
module.exit_json(changed=zone.changed, msg=', '.join(zone.msg))

View file

@ -211,7 +211,7 @@ NA = "N/A"
def get_sorcery_ver(module):
""" Get Sorcery version. """
cmd_sorcery = "%s --version" % SORCERY['sorcery']
cmd_sorcery = f"{SORCERY['sorcery']} --version"
rc, stdout, stderr = module.run_command(cmd_sorcery)
@ -231,7 +231,7 @@ def codex_fresh(codex, module):
for grimoire in codex:
lastupdate_path = os.path.join(SORCERY_STATE_DIR,
grimoire + ".lastupdate")
f"{grimoire}.lastupdate")
try:
mtime = os.stat(lastupdate_path).st_mtime
@ -254,7 +254,7 @@ def codex_list(module, skip_new=False):
codex = {}
cmd_scribe = "%s index" % SORCERY['scribe']
cmd_scribe = f"{SORCERY['scribe']} index"
rc, stdout, stderr = module.run_command(cmd_scribe)
@ -295,12 +295,12 @@ def update_sorcery(module):
else:
sorcery_ver = get_sorcery_ver(module)
cmd_sorcery = "%s update" % SORCERY['sorcery']
cmd_sorcery = f"{SORCERY['sorcery']} update"
rc, stdout, stderr = module.run_command(cmd_sorcery)
if rc != 0:
module.fail_json(msg="unable to update Sorcery: " + stdout)
module.fail_json(msg=f"unable to update Sorcery: {stdout}")
if sorcery_ver != get_sorcery_ver(module):
changed = True
@ -333,15 +333,15 @@ def update_codex(module):
# SILENT is required as a workaround for query() in libgpg
module.run_command_environ_update.update(dict(SILENT='1'))
cmd_scribe = "%s update" % SORCERY['scribe']
cmd_scribe = f"{SORCERY['scribe']} update"
if params['repository']:
cmd_scribe += ' %s' % ' '.join(codex.keys())
cmd_scribe += f" {' '.join(codex.keys())}"
rc, stdout, stderr = module.run_command(cmd_scribe)
if rc != 0:
module.fail_json(msg="unable to update Codex: " + stdout)
module.fail_json(msg=f"unable to update Codex: {stdout}")
if codex != codex_list(module):
changed = True
@ -387,7 +387,7 @@ def match_depends(module):
match = rex.match(d)
if not match:
module.fail_json(msg="wrong depends line for spell '%s'" % spell)
module.fail_json(msg=f"wrong depends line for spell '{spell}'")
# normalize status
if not match.group('status') or match.group('status') == '+':
@ -400,19 +400,19 @@ def match_depends(module):
# drop providers spec
depends_list = [s.split('(')[0] for s in depends]
cmd_gaze = "%s -q version %s" % (SORCERY['gaze'], ' '.join(depends_list))
cmd_gaze = f"{SORCERY['gaze']} -q version {' '.join(depends_list)}"
rc, stdout, stderr = module.run_command(cmd_gaze)
if rc != 0:
module.fail_json(msg="wrong dependencies for spell '%s'" % spell)
module.fail_json(msg=f"wrong dependencies for spell '{spell}'")
fi = fileinput.input(sorcery_depends, inplace=True)
try:
try:
for line in fi:
if line.startswith(spell + ':'):
if line.startswith(f"{spell}:"):
match = None
for d in depends:
@ -426,8 +426,7 @@ def match_depends(module):
d_p = re.escape(d[d_offset:])
# .escape() is needed mostly for the spells like 'libsigc++'
rex = re.compile("%s:(?:%s|%s):(?P<lstatus>on|off):optional:" %
(re.escape(spell), re.escape(d), d_p))
rex = re.compile(f"{re.escape(spell)}:(?:{re.escape(d)}|{d_p}):(?P<lstatus>on|off):optional:")
match = rex.match(line)
@ -460,7 +459,7 @@ def match_depends(module):
try:
with open(sorcery_depends, 'a') as fl:
for k in depends_new:
fl.write("%s:%s:%s:optional::\n" % (spell, k, depends[k]))
fl.write(f"{spell}:{k}:{depends[k]}:optional::\n")
except IOError:
module.fail_json(msg="I/O error on the depends file")
@ -494,19 +493,19 @@ def manage_grimoires(module):
todo = set(grimoires) - set(codex)
if not todo:
return (False, "all grimoire(s) are already %sed" % action[:5])
return (False, f"all grimoire(s) are already {action[:5]}ed")
if module.check_mode:
return (True, "would have %sed grimoire(s)" % action[:5])
return (True, f"would have {action[:5]}ed grimoire(s)")
cmd_scribe = "%s %s %s" % (SORCERY['scribe'], action, ' '.join(todo))
cmd_scribe = f"{SORCERY['scribe']} {action} {' '.join(todo)}"
rc, stdout, stderr = module.run_command(cmd_scribe)
if rc != 0:
module.fail_json(msg="failed to %s one or more grimoire(s): %s" % (action, stdout))
module.fail_json(msg=f"failed to {action} one or more grimoire(s): {stdout}")
return (True, "successfully %sed one or more grimoire(s)" % action[:5])
return (True, f"successfully {action[:5]}ed one or more grimoire(s)")
else:
module.fail_json(msg="unsupported operation on '*' repository value")
else:
@ -517,19 +516,19 @@ def manage_grimoires(module):
grimoire = grimoires[0]
if grimoire in codex:
return (False, "grimoire %s already exists" % grimoire)
return (False, f"grimoire {grimoire} already exists")
if module.check_mode:
return (True, "would have added grimoire %s from %s" % (grimoire, url))
return (True, f"would have added grimoire {grimoire} from {url}")
cmd_scribe = "%s add %s from %s" % (SORCERY['scribe'], grimoire, url)
cmd_scribe = f"{SORCERY['scribe']} add {grimoire} from {url}"
rc, stdout, stderr = module.run_command(cmd_scribe)
if rc != 0:
module.fail_json(msg="failed to add grimoire %s from %s: %s" % (grimoire, url, stdout))
module.fail_json(msg=f"failed to add grimoire {grimoire} from {url}: {stdout}")
return (True, "successfully added grimoire %s from %s" % (grimoire, url))
return (True, f"successfully added grimoire {grimoire} from {url}")
else:
module.fail_json(msg="unsupported operation on repository value")
@ -552,14 +551,14 @@ def manage_spells(module):
if params['state'] == 'latest':
# back up original queue
try:
os.rename(sorcery_queue, sorcery_queue + ".backup")
os.rename(sorcery_queue, f"{sorcery_queue}.backup")
except IOError:
module.fail_json(msg="failed to backup the update queue")
# see update_codex()
module.run_command_environ_update.update(dict(SILENT='1'))
cmd_sorcery = "%s queue" % SORCERY['sorcery']
cmd_sorcery = f"{SORCERY['sorcery']} queue"
rc, stdout, stderr = module.run_command(cmd_sorcery)
@ -574,13 +573,13 @@ def manage_spells(module):
if queue_size != 0:
if module.check_mode:
try:
os.rename(sorcery_queue + ".backup", sorcery_queue)
os.rename(f"{sorcery_queue}.backup", sorcery_queue)
except IOError:
module.fail_json(msg="failed to restore the update queue")
return (True, "would have updated the system")
cmd_cast = "%s --queue" % SORCERY['cast']
cmd_cast = f"{SORCERY['cast']} --queue"
rc, stdout, stderr = module.run_command(cmd_cast)
@ -594,12 +593,12 @@ def manage_spells(module):
if module.check_mode:
return (True, "would have rebuilt the system")
cmd_sorcery = "%s rebuild" % SORCERY['sorcery']
cmd_sorcery = f"{SORCERY['sorcery']} rebuild"
rc, stdout, stderr = module.run_command(cmd_sorcery)
if rc != 0:
module.fail_json(msg="failed to rebuild the system: " + stdout)
module.fail_json(msg=f"failed to rebuild the system: {stdout}")
return (True, "successfully rebuilt the system")
else:
@ -607,14 +606,13 @@ def manage_spells(module):
else:
if params['state'] in ('present', 'latest', 'rebuild', 'absent'):
# extract versions from the 'gaze' command
cmd_gaze = "%s -q version %s" % (SORCERY['gaze'], ' '.join(spells))
cmd_gaze = f"{SORCERY['gaze']} -q version {' '.join(spells)}"
rc, stdout, stderr = module.run_command(cmd_gaze)
# fail if any of spells cannot be found
if rc != 0:
module.fail_json(msg="failed to locate spell(s) in the list (%s)" %
', '.join(spells))
module.fail_json(msg=f"failed to locate spell(s) in the list ({', '.join(spells)})")
cast_queue = []
dispel_queue = []
@ -665,12 +663,12 @@ def manage_spells(module):
if module.check_mode:
return (True, "would have cast spell(s)")
cmd_cast = "%s -c %s" % (SORCERY['cast'], ' '.join(cast_queue))
cmd_cast = f"{SORCERY['cast']} -c {' '.join(cast_queue)}"
rc, stdout, stderr = module.run_command(cmd_cast)
if rc != 0:
module.fail_json(msg="failed to cast spell(s): " + stdout)
module.fail_json(msg=f"failed to cast spell(s): {stdout}")
return (True, "successfully cast spell(s)")
elif params['state'] != 'absent':
@ -680,12 +678,12 @@ def manage_spells(module):
if module.check_mode:
return (True, "would have dispelled spell(s)")
cmd_dispel = "%s %s" % (SORCERY['dispel'], ' '.join(dispel_queue))
cmd_dispel = f"{SORCERY['dispel']} {' '.join(dispel_queue)}"
rc, stdout, stderr = module.run_command(cmd_dispel)
if rc != 0:
module.fail_json(msg="failed to dispel spell(s): " + stdout)
module.fail_json(msg=f"failed to dispel spell(s): {stdout}")
return (True, "successfully dispelled spell(s)")
else:
@ -751,7 +749,7 @@ def main():
state_msg = "no change in state"
state_changed = False
module.exit_json(changed=state_changed, msg=state_msg + ": " + '; '.join(x[1] for x in changed.values()))
module.exit_json(changed=state_changed, msg=f"{state_msg}: {'; '.join((x[1] for x in changed.values()))}")
if __name__ == '__main__':

View file

@ -138,7 +138,7 @@ def request(resource, xml=None, method=None):
"Accept": "application/xml"
}
url = module.params['oneclick_url'] + '/spectrum/restful/' + resource
url = f"{module.params['oneclick_url']}/spectrum/restful/{resource}"
response, info = fetch_url(module, url, data=xml, method=method, headers=headers, timeout=45)
@ -163,7 +163,7 @@ def get_ip():
try:
device_ip = gethostbyname(module.params.get('device'))
except gaierror:
module.fail_json(msg="failed to resolve device ip address for '%s'" % module.params.get('device'))
module.fail_json(msg=f"failed to resolve device ip address for '{module.params.get('device')}'")
return device_ip
@ -171,10 +171,10 @@ def get_ip():
def get_device(device_ip):
"""Query OneClick for the device using the IP Address"""
resource = '/models'
landscape_min = "0x%x" % int(module.params.get('landscape'), 16)
landscape_max = "0x%x" % (int(module.params.get('landscape'), 16) + 0x100000)
landscape_min = f"0x{int(module.params.get('landscape'), 16):x}"
landscape_max = f"0x{int(module.params.get('landscape'), 16) + 0x100000:x}"
xml = """<?xml version="1.0" encoding="UTF-8"?>
xml = f"""<?xml version="1.0" encoding="UTF-8"?>
<rs:model-request throttlesize="5"
xmlns:rs="http://www.ca.com/spectrum/restful/schema/request"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@ -190,19 +190,19 @@ def get_device(device_ip):
</equals>
<greater-than>
<attribute id="0x129fa">
<value>{mh_min}</value>
<value>{landscape_min}</value>
</attribute>
</greater-than>
<less-than>
<attribute id="0x129fa">
<value>{mh_max}</value>
<value>{landscape_max}</value>
</attribute>
</less-than>
</and>
</filtered-models>
<action>FIND_DEV_MODELS_BY_IP</action>
<attribute id="AttributeID.NETWORK_ADDRESS">
<value>{search_ip}</value>
<value>{device_ip}</value>
</attribute>
</action-models>
</rs:search-criteria>
@ -210,7 +210,7 @@ def get_device(device_ip):
</rs:target-models>
<rs:requested-attribute id="0x12d7f" /> <!--Network Address-->
</rs:model-request>
""".format(search_ip=device_ip, mh_min=landscape_min, mh_max=landscape_max)
"""
result = post(resource, xml=xml)
@ -225,7 +225,7 @@ def get_device(device_ip):
model = root.find('ca:model-responses', namespace).find('ca:model', namespace)
if model.get('error'):
module.fail_json(msg="error checking device: %s" % model.get('error'))
module.fail_json(msg=f"error checking device: {model.get('error')}")
# get the attributes
model_handle = model.get('mh')
@ -233,7 +233,7 @@ def get_device(device_ip):
model_address = model.find('./*[@id="0x12d7f"]').text
# derive the landscape handler from the model handler of the device
model_landscape = "0x%x" % int(int(model_handle, 16) // 0x100000 * 0x100000)
model_landscape = f"0x{int(model_handle, 16) // 0x100000 * 0x100000:x}"
device = dict(
model_handle=model_handle,
@ -254,14 +254,14 @@ def add_device():
device = dict(
model_handle=None,
address=device_ip,
landscape="0x%x" % int(module.params.get('landscape'), 16))
landscape=f"0x{int(module.params.get('landscape'), 16):x}")
module.exit_json(changed=True, device=device)
resource = 'model?ipaddress=' + device_ip + '&commstring=' + module.params.get('community')
resource += '&landscapeid=' + module.params.get('landscape')
resource = f"model?ipaddress={device_ip}&commstring={module.params.get('community')}"
resource += f"&landscapeid={module.params.get('landscape')}"
if module.params.get('agentport', None):
resource += '&agentport=' + str(module.params.get('agentport', 161))
resource += f"&agentport={module.params.get('agentport', 161)}"
result = post(resource)
root = ET.fromstring(result)
@ -273,7 +273,7 @@ def add_device():
model = root.find('ca:model', namespace)
model_handle = model.get('mh')
model_landscape = "0x%x" % int(int(model_handle, 16) // 0x100000 * 0x100000)
model_landscape = f"0x{int(model_handle, 16) // 0x100000 * 0x100000:x}"
device = dict(
model_handle=model_handle,
@ -294,7 +294,7 @@ def remove_device():
if module.check_mode:
module.exit_json(changed=True)
resource = '/model/' + device['model_handle']
resource = f"/model/{device['model_handle']}"
result = delete(resource)
root = ET.fromstring(result)
@ -304,7 +304,7 @@ def remove_device():
if error != 'Success':
error_message = root.find('ca:error-message', namespace).text
module.fail_json(msg="%s %s" % (error, error_message))
module.fail_json(msg=f"{error} {error_message}")
module.exit_json(changed=True)

View file

@ -143,7 +143,6 @@ changed_attrs:
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.urls import fetch_url
from urllib.parse import quote
import json
@ -158,7 +157,7 @@ class spectrum_model_attrs:
# If the user did not define a full path to the restul space in url:
# params, add what we believe it to be.
if not re.search('\\/.+', self.url.split('://')[1]):
self.url = "%s/spectrum/restful" % self.url.rstrip('/')
self.url = f"{self.url.rstrip('/')}/spectrum/restful"
# Align these with what is defined in OneClick's UI under:
# Locator -> Devices -> By Model Name -> <enter any model> ->
# Attributes tab.
@ -208,7 +207,7 @@ class spectrum_model_attrs:
:rtype: str
"""
return "%s/%s" % (self.url.rstrip('/'), path.lstrip('/'))
return f"{self.url.rstrip('/')}/{path.lstrip('/')}"
def attr_id(self, name):
"""
@ -261,7 +260,7 @@ class spectrum_model_attrs:
"""
# Build the update URL
update_url = self.build_url("/model/%s?" % model_handle)
update_url = self.build_url(f"/model/{model_handle}?")
for name, val in list(attrs.items()):
if val is None:
# None values should be converted to empty strings
@ -270,7 +269,7 @@ class spectrum_model_attrs:
if not update_url.endswith('?'):
update_url += "&"
update_url += "attr=%s&val=%s" % (self.attr_id(name) or name, val)
update_url += f"attr={self.attr_id(name) or name}&val={val}"
# POST to /model to update the attributes, or fail.
resp, info = fetch_url(self.module, update_url, method="PUT",
@ -283,7 +282,7 @@ class spectrum_model_attrs:
else:
body = "" if resp is None else resp.read()
if status_code != 200:
self.result['msg'] = "HTTP PUT error %s: %s: %s" % (status_code, update_url, body)
self.result['msg'] = f"HTTP PUT error {status_code}: {update_url}: {body}"
self.module.fail_json(**self.result)
# Load and parse the JSON response and either fail or set results.
@ -330,7 +329,7 @@ class spectrum_model_attrs:
rqstd_attrs += '<rs:requested-attribute id="%s" />' % (self.attr_id(ra) or ra)
# Build the complete XML search query for HTTP POST.
xml = """<?xml version="1.0" encoding="UTF-8"?>
xml = f"""<?xml version="1.0" encoding="UTF-8"?>
<rs:model-request throttlesize="5"
xmlns:rs="http://www.ca.com/spectrum/restful/schema/request"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@ -338,13 +337,13 @@ xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../x
<rs:target-models>
<rs:models-search>
<rs:search-criteria xmlns="http://www.ca.com/spectrum/restful/schema/filter">
{0}
{search_criteria}
</rs:search-criteria>
</rs:models-search>
</rs:target-models>
{1}
{rqstd_attrs}
</rs:model-request>
""".format(search_criteria, rqstd_attrs)
"""
# POST to /models and fail on errors.
url = self.build_url("/models")
@ -358,7 +357,7 @@ xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../x
else:
body = "" if resp is None else resp.read()
if status_code != 200:
self.result['msg'] = "HTTP POST error %s: %s: %s" % (status_code, url, body)
self.result['msg'] = f"HTTP POST error {status_code}: {url}: {body}"
self.module.fail_json(**self.result)
# Parse through the XML response and fail on any detected errors.
@ -367,15 +366,13 @@ xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../x
error = root.attrib['error']
model_responses = root.find('ca:model-responses', self.resp_namespace)
if total_models < 1:
self.result['msg'] = "No models found matching search criteria `%s'" % search_criteria
self.result['msg'] = f"No models found matching search criteria `{search_criteria}'"
self.module.fail_json(**self.result)
elif total_models > 1:
self.result['msg'] = "More than one model found (%s): `%s'" % (total_models, ET.tostring(model_responses,
encoding='unicode'))
self.result['msg'] = f"More than one model found ({total_models}): `{ET.tostring(model_responses, encoding='unicode')}'"
self.module.fail_json(**self.result)
if error != "EndOfResults":
self.result['msg'] = "Unexpected search response `%s': %s" % (error, ET.tostring(model_responses,
encoding='unicode'))
self.result['msg'] = f"Unexpected search response `{error}': {ET.tostring(model_responses, encoding='unicode')}"
self.module.fail_json(**self.result)
model = model_responses.find('ca:model', self.resp_namespace)
attrs = model.findall('ca:attribute', self.resp_namespace)
@ -516,9 +513,7 @@ def run_module():
sm = spectrum_model_attrs(module)
sm.ensure_model_attrs()
except Exception as e:
module.fail_json(msg="Failed to ensure attribute(s) on `%s' with "
"exception: %s" % (module.params['name'],
to_native(e)))
module.fail_json(msg=f"Failed to ensure attribute(s) on `{module.params['name']}' with exception: {e}")
def main():

View file

@ -895,7 +895,7 @@ def handle_elastigroup(client, module):
if should_create is True:
if state == 'present':
eg = expand_elastigroup(module, is_update=False)
module.debug(str(" [INFO] " + message + "\n"))
module.debug(f" [INFO] {message}\n")
group = client.create_elastigroup(group=eg)
group_id = group['id']
message = 'Created group Successfully.'
@ -923,7 +923,7 @@ def handle_elastigroup(client, module):
message = 'Updated and started rolling the group successfully.'
except SpotinstClientException as exc:
message = 'Updated group successfully, but failed to perform roll. Error:' + str(exc)
message = f"Updated group successfully, but failed to perform roll. Error:{exc}"
has_changed = True
elif state == 'absent':
@ -933,7 +933,7 @@ def handle_elastigroup(client, module):
if "GROUP_DOESNT_EXIST" in exc.message:
pass
else:
module.fail_json(msg="Error while attempting to delete group : " + exc.message)
module.fail_json(msg=f"Error while attempting to delete group : {exc.message}")
message = 'Deleted group successfully.'
has_changed = True

View file

@ -163,7 +163,7 @@ def create_cpg(
disk_type):
try:
if not validate_set_size(raid_type, set_size):
return (False, False, "Set size %s not part of RAID set %s" % (set_size, raid_type))
return (False, False, f"Set size {set_size} not part of RAID set {raid_type}")
if not client_obj.cpgExists(cpg_name):
disk_patterns = []
@ -195,8 +195,8 @@ def create_cpg(
else:
return (True, False, "CPG already present")
except exceptions.ClientException as e:
return (False, False, "CPG creation failed | %s" % (e))
return (True, True, "Created CPG %s successfully." % cpg_name)
return (False, False, f"CPG creation failed | {e}")
return (True, True, f"Created CPG {cpg_name} successfully.")
def delete_cpg(
@ -208,8 +208,8 @@ def delete_cpg(
else:
return (True, False, "CPG does not exist")
except exceptions.ClientException as e:
return (False, False, "CPG delete failed | %s" % e)
return (True, True, "Deleted CPG %s successfully." % cpg_name)
return (False, False, f"CPG delete failed | {e}")
return (True, True, f"Deleted CPG {cpg_name} successfully.")
def main():
@ -235,7 +235,7 @@ def main():
disk_type = module.params["disk_type"]
secure = module.params["secure"]
wsapi_url = 'https://%s:8080/api/v1' % storage_system_ip
wsapi_url = f'https://{storage_system_ip}:8080/api/v1'
try:
client_obj = client.HPE3ParClient(wsapi_url, secure)
except exceptions.SSLCertFailed:
@ -245,7 +245,7 @@ def main():
except exceptions.UnsupportedVersion:
module.fail_json(msg="Unsupported WSAPI version")
except Exception as e:
module.fail_json(msg="Initializing client failed. %s" % e)
module.fail_json(msg=f"Initializing client failed. {e}")
if storage_system_username is None or storage_system_password is None:
module.fail_json(msg="Storage system username or password is None")
@ -269,7 +269,7 @@ def main():
disk_type
)
except Exception as e:
module.fail_json(msg="CPG create failed | %s" % e)
module.fail_json(msg=f"CPG create failed | {e}")
finally:
client_obj.logout()
@ -281,7 +281,7 @@ def main():
cpg_name
)
except Exception as e:
module.fail_json(msg="CPG create failed | %s" % e)
module.fail_json(msg=f"CPG create failed | {e}")
finally:
client_obj.logout()

View file

@ -224,7 +224,6 @@ import os
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.general.plugins.module_utils._stormssh import ConfigParser, HAS_PARAMIKO, PARAMIKO_IMPORT_ERROR
from ansible_collections.community.general.plugins.module_utils.ssh import determine_config_file
@ -260,7 +259,7 @@ class SSHConfig(object):
try:
self.config = ConfigParser(self.config_file)
except FileNotFoundError:
self.module.fail_json(msg="Failed to find %s" % self.config_file)
self.module.fail_json(msg=f"Failed to find {self.config_file}")
self.config.load()
def check_ssh_config_path(self):
@ -272,7 +271,7 @@ class SSHConfig(object):
self.identity_file = os.path.join(dirname, self.identity_file)
if not os.path.exists(self.identity_file):
self.module.fail_json(msg='IdentityFile %s does not exist' % self.params['identity_file'])
self.module.fail_json(msg=f"IdentityFile {self.params['identity_file']} does not exist")
def ensure_state(self):
hosts_result = self.config.search_host(self.host)
@ -298,14 +297,13 @@ class SSHConfig(object):
if self.params.get('other_options'):
for key, value in self.params.get('other_options').items():
if key.lower() != key:
self.module.fail_json(msg="The other_options key {key!r} must be lower case".format(key=key))
self.module.fail_json(msg=f"The other_options key {key} must be lower case")
if key not in args:
if not isinstance(value, str):
self.module.fail_json(msg="The other_options value provided for key {key!r} must be a string, got {type}".format(key=key,
type=type(value)))
self.module.fail_json(msg=f"The other_options value provided for key {key} must be a string, got {type(value)}")
args[key] = value
else:
self.module.fail_json(msg="Multiple values provided for key {key!r}".format(key=key))
self.module.fail_json(msg=f"Multiple values provided for key {key}")
config_changed = False
hosts_changed = []
@ -349,7 +347,7 @@ class SSHConfig(object):
self.config.write_to_ssh_config()
except PermissionError as perm_exec:
self.module.fail_json(
msg="Failed to write to %s due to permission issue: %s" % (self.config_file, to_native(perm_exec)))
msg=f"Failed to write to {self.config_file} due to permission issue: {perm_exec}")
# Make sure we set the permission
perm_mode = '0600'
if self.config_file == '/etc/ssh/ssh_config':

View file

@ -157,7 +157,7 @@ class StackiHost(object):
'Content-type': 'application/x-www-form-urlencoded', 'Cookie': cred_a.headers.get('Set-Cookie')}
# Endpoint to get final authentication header
login_endpoint = self.endpoint + "/login"
login_endpoint = f"{self.endpoint}/login"
# Get Final CSRF and Session ID
login_req = self.do_request(login_endpoint, headers=header, payload=urlencode(auth_creds), method='POST')
@ -194,7 +194,7 @@ class StackiHost(object):
self.do_request(self.endpoint, payload=json.dumps({"cmd": "sync host config"}), headers=self.header, method="POST")
def stack_force_install(self, result):
data = {'cmd': "set host boot {0} action=install".format(self.hostname)}
data = {'cmd': f"set host boot {self.hostname} action=install"}
self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST")
changed = True
@ -207,8 +207,7 @@ class StackiHost(object):
data = dict()
changed = False
data['cmd'] = "add host {0} rack={1} rank={2} appliance={3}"\
.format(self.hostname, self.rack, self.rank, self.appliance)
data['cmd'] = f"add host {self.hostname} rack={self.rack} rank={self.rank} appliance={self.appliance}"
self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST")
self.stack_sync()
@ -219,8 +218,7 @@ class StackiHost(object):
def stack_remove(self, result):
data = dict()
data['cmd'] = "remove host {0}"\
.format(self.hostname)
data['cmd'] = f"remove host {self.hostname}"
self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST")
self.stack_sync()
@ -260,15 +258,14 @@ def main():
stacki.stack_force_install(result)
# If state is present, but host exists, and force_install and false, do nothing
elif module.params['state'] == 'present' and host_exists and not module.params['force_install']:
result['stdout'] = "{0} already exists. Set 'force_install' to true to bootstrap"\
.format(module.params['name'])
result['stdout'] = f"{module.params['name']} already exists. Set 'force_install' to true to bootstrap"
# Otherwise, state is present, but host doesn't exists, require more params to add host
elif module.params['state'] == 'present' and not host_exists:
for param in ['appliance', 'rack', 'rank', 'prim_intf', 'prim_intf_ip', 'network', 'prim_intf_mac']:
if not module.params[param]:
missing_params.append(param)
if len(missing_params) > 0:
module.fail_json(msg="missing required arguments: {0}".format(missing_params))
module.fail_json(msg=f"missing required arguments: {missing_params}")
stacki.stack_add(result)
# If state is absent, and host exists, lets remove it.

View file

@ -153,8 +153,8 @@ def main():
elif protocol == 'tcp':
client = tcp_statsd_client(host=host, port=port, timeout=timeout, prefix=metric_prefix, ipv6=False)
metric_name = '%s/%s' % (metric_prefix, metric) if metric_prefix else metric
metric_display_value = '%s (delta=%s)' % (value, delta) if metric_type == 'gauge' else value
metric_name = f'{metric_prefix}/{metric}' if metric_prefix else metric
metric_display_value = f'{value} (delta={delta})' if metric_type == 'gauge' else value
try:
if metric_type == 'counter':
@ -163,13 +163,13 @@ def main():
client.gauge(metric, value, delta=delta)
except Exception as exc:
module.fail_json(msg='Failed sending to StatsD %s' % str(exc))
module.fail_json(msg=f'Failed sending to StatsD {exc}')
finally:
if protocol == 'tcp':
client.close()
module.exit_json(msg="Sent %s %s -> %s to StatsD" % (metric_type, metric_name, str(metric_display_value)), changed=True)
module.exit_json(msg=f"Sent {metric_type} {metric_name} -> {metric_display_value!s} to StatsD", changed=True)
if __name__ == '__main__':

View file

@ -196,7 +196,7 @@ def get_api_auth_headers(api_id, api_key, url, statuspage):
try:
response = open_url(
url + "/v2/component/list/" + statuspage, headers=headers)
f"{url}/v2/component/list/{statuspage}", headers=headers)
data = json.loads(response.read())
if data['status']['message'] == 'Authentication failed':
return 1, None, None, "Authentication failed: " \
@ -288,7 +288,7 @@ def create_maintenance(auth_headers, url, statuspage, host_ids,
for val in host_ids:
component_id.append(val['component_id'])
container_id.append(val['container_id'])
infrastructure_id = [i + '-' + j for i, j in zip(component_id, container_id)]
infrastructure_id = [f"{i}-{j}" for i, j in zip(component_id, container_id)]
try:
values = json.dumps({
"statuspage_id": statuspage,
@ -307,7 +307,7 @@ def create_maintenance(auth_headers, url, statuspage, host_ids,
"maintenance_notify_1_hr": str(int(maintenance_notify_1_hr))
})
response = open_url(
url + "/v2/maintenance/schedule", data=values,
f"{url}/v2/maintenance/schedule", data=values,
headers=auth_headers)
data = json.loads(response.read())
@ -325,7 +325,7 @@ def delete_maintenance(auth_headers, url, statuspage, maintenance_id):
"maintenance_id": maintenance_id,
})
response = open_url(
url=url + "/v2/maintenance/delete",
url=f"{url}/v2/maintenance/delete",
data=values,
headers=auth_headers)
data = json.loads(response.read())
@ -388,7 +388,7 @@ def main():
(rc, auth_headers, auth_content, error) = \
get_api_auth_headers(api_id, api_key, url, statuspage)
if rc != 0:
module.fail_json(msg="Failed to get auth keys: %s" % error)
module.fail_json(msg=f"Failed to get auth keys: {error}")
else:
auth_headers = {}
auth_content = {}
@ -397,7 +397,7 @@ def main():
(rc, returned_date, error) = get_date_time(
start_date, start_time, minutes)
if rc != 0:
module.fail_json(msg="Failed to set date/time: %s" % error)
module.fail_json(msg=f"Failed to set date/time: {error}")
if not components and not containers:
return module.fail_json(msg="A Component or Container must be "
@ -410,13 +410,13 @@ def main():
(rc, host_ids, error) = get_component_ids(auth_content,
components)
if rc != 0:
module.fail_json(msg="Failed to find component %s" % error)
module.fail_json(msg=f"Failed to find component {error}")
if containers:
(rc, host_ids, error) = get_container_ids(auth_content,
containers)
if rc != 0:
module.fail_json(msg="Failed to find container %s" % error)
module.fail_json(msg=f"Failed to find container {error}")
if module.check_mode:
module.exit_json(changed=True)
@ -431,8 +431,7 @@ def main():
module.exit_json(changed=True, result="Successfully created "
"maintenance")
else:
module.fail_json(msg="Failed to create maintenance: %s"
% error)
module.fail_json(msg=f"Failed to create maintenance: {error}")
if state == "absent":
@ -440,7 +439,7 @@ def main():
(rc, auth_headers, auth_content, error) = \
get_api_auth_headers(api_id, api_key, url, statuspage)
if rc != 0:
module.fail_json(msg="Failed to get auth keys: %s" % error)
module.fail_json(msg=f"Failed to get auth keys: {error}")
else:
auth_headers = {}
@ -456,7 +455,7 @@ def main():
)
else:
module.fail_json(
msg="Failed to delete maintenance: %s" % error)
msg=f"Failed to delete maintenance: {error}")
if __name__ == '__main__':

View file

@ -215,22 +215,14 @@ class Sudoers(object):
if self.user:
owner = self.user
elif self.group:
owner = '%{group}'.format(group=self.group)
owner = f'%{self.group}'
commands_str = ', '.join(self.commands)
noexec_str = 'NOEXEC:' if self.noexec else ''
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
setenv_str = 'SETENV:' if self.setenv else ''
runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
return "{owner} {host}={runas}{noexec}{nopasswd}{setenv} {commands}\n".format(
owner=owner,
host=self.host,
runas=runas_str,
noexec=noexec_str,
nopasswd=nopasswd_str,
setenv=setenv_str,
commands=commands_str
)
runas_str = f'({self.runas})' if self.runas is not None else ''
return f"{owner} {self.host}={runas_str}{noexec_str}{nopasswd_str}{setenv_str} {commands_str}\n"
def validate(self):
if self.validation == 'absent':
@ -244,7 +236,7 @@ class Sudoers(object):
rc, stdout, stderr = self.module.run_command(check_command, data=self.content())
if rc != 0:
self.module.fail_json(msg='Failed to validate sudoers rule:\n{stdout}'.format(stdout=stdout or stderr), stdout=stdout, stderr=stderr)
self.module.fail_json(msg=f'Failed to validate sudoers rule:\n{stdout or stderr}', stdout=stdout, stderr=stderr)
def run(self):
if self.state == 'absent':

View file

@ -158,7 +158,7 @@ def main():
supervisorctl_args = [supervisorctl_path]
else:
module.fail_json(
msg="Provided path to supervisorctl does not exist or isn't executable: %s" % supervisorctl_path)
msg=f"Provided path to supervisorctl does not exist or isn't executable: {supervisorctl_path}")
else:
supervisorctl_args = [module.get_bin_path('supervisorctl', True)]
@ -222,7 +222,7 @@ def main():
module.exit_json(changed=True)
for process_name in to_take_action_on:
rc, out, err = run_supervisorctl(action, process_name, check_rc=True)
if '%s: %s' % (process_name, expected_result) not in out:
if f'{process_name}: {expected_result}' not in out:
module.fail_json(msg=out)
if exit_module:
@ -249,7 +249,7 @@ def main():
module.exit_json(changed=True)
run_supervisorctl('reread', check_rc=True)
rc, out, err = run_supervisorctl('remove', name)
if '%s: removed process group' % name in out:
if f'{name}: removed process group' in out:
module.exit_json(changed=True, name=name, state=state)
else:
module.fail_json(msg=out, name=name, state=state)
@ -262,7 +262,7 @@ def main():
module.exit_json(changed=True)
run_supervisorctl('reread', check_rc=True)
dummy, out, dummy = run_supervisorctl('add', name)
if '%s: added process group' % name in out:
if f'{name}: added process group' in out:
module.exit_json(changed=True, name=name, state=state)
else:
module.fail_json(msg=out, name=name, state=state)
@ -278,7 +278,7 @@ def main():
take_action_on_processes(processes, lambda s: s in ('RUNNING', 'STARTING'), 'stop', 'stopped')
if state == 'signalled':
take_action_on_processes(processes, lambda s: s in ('RUNNING',), "signal %s" % signal, 'signalled')
take_action_on_processes(processes, lambda s: s in ('RUNNING',), f"signal {signal}", 'signalled')
if __name__ == '__main__':

View file

@ -143,15 +143,15 @@ class Svc(object):
self.svc_cmd = module.get_bin_path('svc', opt_dirs=self.extra_paths)
self.svstat_cmd = module.get_bin_path('svstat', opt_dirs=self.extra_paths)
self.svc_full = '/'.join([self.service_dir, self.name])
self.src_full = '/'.join([self.service_src, self.name])
self.svc_full = f"{self.service_dir}/{self.name}"
self.src_full = f"{self.service_src}/{self.name}"
self.enabled = os.path.lexists(self.svc_full)
if self.enabled:
self.downed = os.path.lexists('%s/down' % self.svc_full)
self.downed = os.path.lexists(f'{self.svc_full}/down')
self.get_status()
else:
self.downed = os.path.lexists('%s/down' % self.src_full)
self.downed = os.path.lexists(f'{self.src_full}/down')
self.state = 'stopped'
def enable(self):
@ -159,18 +159,18 @@ class Svc(object):
try:
os.symlink(self.src_full, self.svc_full)
except OSError as e:
self.module.fail_json(path=self.src_full, msg='Error while linking: %s' % to_native(e))
self.module.fail_json(path=self.src_full, msg=f'Error while linking: {to_native(e)}')
else:
self.module.fail_json(msg="Could not find source for service to enable (%s)." % self.src_full)
self.module.fail_json(msg=f"Could not find source for service to enable ({self.src_full}).")
def disable(self):
try:
os.unlink(self.svc_full)
except OSError as e:
self.module.fail_json(path=self.svc_full, msg='Error while unlinking: %s' % to_native(e))
self.module.fail_json(path=self.svc_full, msg=f'Error while unlinking: {e}')
self.execute_command([self.svc_cmd, '-dx', self.src_full])
src_log = '%s/log' % self.src_full
src_log = f'{self.src_full}/log'
if os.path.exists(src_log):
self.execute_command([self.svc_cmd, '-dx', src_log])
@ -228,7 +228,7 @@ class Svc(object):
try:
rc, out, err = self.module.run_command(cmd)
except Exception as e:
self.module.fail_json(msg="failed to execute: %s" % to_native(e), exception=traceback.format_exc())
self.module.fail_json(msg=f"failed to execute: {e}", exception=traceback.format_exc())
return (rc, out, err)
def report(self):
@ -274,7 +274,7 @@ def main():
else:
svc.disable()
except (OSError, IOError) as e:
module.fail_json(msg="Could not change service link: %s" % to_native(e))
module.fail_json(msg=f"Could not change service link: {e}")
if state is not None and state != svc.state:
changed = True
@ -284,14 +284,14 @@ def main():
if downed is not None and downed != svc.downed:
changed = True
if not module.check_mode:
d_file = "%s/down" % svc.svc_full
d_file = f"{svc.svc_full}/down"
try:
if downed:
open(d_file, "a").close()
else:
os.unlink(d_file)
except (OSError, IOError) as e:
module.fail_json(msg="Could not change downed file: %s " % (to_native(e)))
module.fail_json(msg=f"Could not change downed file: {e} ")
module.exit_json(changed=changed, svc=svc.report())

View file

@ -223,7 +223,7 @@ def main():
# Stdout is normally empty but for some packages can be
# very long and is not often useful
if len(out) > 75:
out = out[:75] + '...'
out = f"{out[:75]}..."
elif state == 'absent':
if package_installed(module, name, category):

View file

@ -180,14 +180,14 @@ def main():
rc, output = install_package(module, depot, name)
if not rc:
msg = "Package upgraded, Before " + version_installed + " Now " + version_depot
msg = f"Package upgraded, Before {version_installed} Now {version_depot}"
changed = True
else:
module.fail_json(name=name, msg=output, rc=rc)
else:
output = "Software package not in repository " + depot
output = f"Software package not in repository {depot}"
module.fail_json(name=name, msg=output, rc=rc)
elif state == 'absent' and installed is True:

View file

@ -131,22 +131,22 @@ class Swupd(object):
cmd = [self.swupd_cmd] + command
if self.format:
cmd.append("--format=%s" % self.format)
cmd.append(f"--format={self.format}")
if self.manifest:
cmd.append("--manifest=%s" % self.manifest)
cmd.append(f"--manifest={self.manifest}")
if self.url:
cmd.append("--url=%s" % self.url)
cmd.append(f"--url={self.url}")
else:
if self.contenturl and command != "check-update":
cmd.append("--contenturl=%s" % self.contenturl)
cmd.append(f"--contenturl={self.contenturl}")
if self.versionurl:
cmd.append("--versionurl=%s" % self.versionurl)
cmd.append(f"--versionurl={self.versionurl}")
return cmd
def _is_bundle_installed(self, bundle):
try:
os.stat("/usr/share/clear/bundles/%s" % bundle)
os.stat(f"/usr/share/clear/bundles/{bundle}")
except OSError:
return False
@ -184,7 +184,7 @@ class Swupd(object):
self.module.exit_json(changed=not self._is_bundle_installed(bundle))
if self._is_bundle_installed(bundle):
self.msg = "Bundle %s is already installed" % bundle
self.msg = f"Bundle {bundle} is already installed"
return
cmd = self._get_cmd(["bundle-add", bundle])
@ -192,11 +192,11 @@ class Swupd(object):
if self.rc == 0:
self.changed = True
self.msg = "Bundle %s installed" % bundle
self.msg = f"Bundle {bundle} installed"
return
self.failed = True
self.msg = "Failed to install bundle %s" % bundle
self.msg = f"Failed to install bundle {bundle}"
def remove_bundle(self, bundle):
"""Removes a bundle with `swupd bundle-remove bundle`"""
@ -212,11 +212,11 @@ class Swupd(object):
if self.rc == 0:
self.changed = True
self.msg = "Bundle %s removed" % bundle
self.msg = f"Bundle {bundle} removed"
return
self.failed = True
self.msg = "Failed to remove bundle %s" % bundle
self.msg = f"Failed to remove bundle {bundle}"
def update_os(self):
"""Updates the os with `swupd update`"""

View file

@ -120,7 +120,6 @@ import syslog
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native
def get_facility(facility):
@ -202,7 +201,7 @@ def main():
result['changed'] = True
except Exception as exc:
module.fail_json(error='Failed to write to syslog %s' % to_native(exc), exception=traceback.format_exc(), **result)
module.fail_json(error=f'Failed to write to syslog {exc}', exception=traceback.format_exc(), **result)
module.exit_json(**result)

View file

@ -106,7 +106,7 @@ def syspatch_run(module):
rc, out, err = module.run_command([cmd] + check_flag)
if rc != 0:
module.fail_json(msg="Command %s failed rc=%d, out=%s, err=%s" % (cmd, rc, out, err))
module.fail_json(msg=f"Command {cmd} failed rc={rc}, out={out}, err={err}")
if len(out) > 0:
# Changes pending
@ -123,7 +123,7 @@ def syspatch_run(module):
# Workaround syspatch ln bug:
# http://openbsd-archive.7691.n7.nabble.com/Warning-applying-latest-syspatch-td354250.html
if rc != 0 and err != 'ln: /usr/X11R6/bin/X: No such file or directory\n':
module.fail_json(msg="Command %s failed rc=%d, out=%s, err=%s" % (cmd, rc, out, err))
module.fail_json(msg=f"Command {cmd} failed rc={rc}, out={out}, err={err}")
elif out.lower().find('create unique kernel') >= 0:
# Kernel update applied
reboot_needed = True

View file

@ -147,8 +147,8 @@ class Sysrc(StateModuleHelper):
return None
def _modify(self, op, changed):
(rc, out, err) = self._sysrc("%s%s=%s%s" % (self.vars.name, op, self.vars.delim, self.vars.value))
if out.startswith("%s:" % self.vars.name):
(rc, out, err) = self._sysrc(f"{self.vars.name}{op}={self.vars.delim}{self.vars.value}")
if out.startswith(f"{self.vars.name}:"):
return changed(out.split(' -> ')[1].strip().split(self.vars.delim))
return False
@ -161,7 +161,7 @@ class Sysrc(StateModuleHelper):
(rc, out, err) = self.module.run_command(cmd)
if "Permission denied" in err:
self.module.fail_json(msg="Permission denied for %s" % self.vars.path)
self.module.fail_json(msg=f"Permission denied for {self.vars.path}")
return rc, out, err
@ -184,7 +184,7 @@ class Sysrc(StateModuleHelper):
return
if not self.check_mode:
self._sysrc("%s=%s" % (self.vars.name, self.vars.value))
self._sysrc(f"{self.vars.name}={self.vars.value}")
self.changed = True

View file

@ -119,16 +119,16 @@ def main():
decrypt_cmd = [cmd, "decrypt"]
if name:
decrypt_cmd.append("--name=" + name)
decrypt_cmd.append(f"--name={name}")
else:
decrypt_cmd.append("--name=")
decrypt_cmd.append("--newline=" + ("yes" if newline else "no"))
decrypt_cmd.append(f"--newline={'yes' if newline else 'no'}")
if timestamp:
decrypt_cmd.append("--timestamp=" + timestamp)
decrypt_cmd.append(f"--timestamp={timestamp}")
if transcode:
decrypt_cmd.append("--transcode=" + transcode)
decrypt_cmd.append(f"--transcode={transcode}")
if user:
decrypt_cmd.append("--uid=" + user)
decrypt_cmd.append(f"--uid={user}")
decrypt_cmd.extend(["-", "-"])
rc, stdout, stderr = module.run_command(decrypt_cmd, data=secret, binary_data=True)

View file

@ -115,17 +115,17 @@ def main():
encrypt_cmd = [cmd, "encrypt"]
if name:
encrypt_cmd.append("--name=" + name)
encrypt_cmd.append(f"--name={name}")
else:
encrypt_cmd.append("--name=")
if not_after:
encrypt_cmd.append("--not-after=" + not_after)
encrypt_cmd.append(f"--not-after={not_after}")
if pretty:
encrypt_cmd.append("--pretty")
if timestamp:
encrypt_cmd.append("--timestamp=" + timestamp)
encrypt_cmd.append(f"--timestamp={timestamp}")
if user:
encrypt_cmd.append("--uid=" + user)
encrypt_cmd.append(f"--uid={user}")
encrypt_cmd.extend(["-", "-"])
rc, stdout, stderr = module.run_command(encrypt_cmd, data=secret, binary_data=True)

View file

@ -296,12 +296,12 @@ def get_category_base_props(category):
def validate_unit_and_properties(runner, unit, extra_properties, units_info, property_cache):
if not unit_exists(unit, units_info):
module.fail_json(msg="Unit '{0}' does not exist or is inaccessible.".format(unit))
module.fail_json(msg=f"Unit '{unit}' does not exist or is inaccessible.")
category = determine_category(unit)
if not category:
module.fail_json(msg="Could not determine the category for unit '{0}'.".format(unit))
module.fail_json(msg=f"Could not determine the category for unit '{unit}'.")
state_props = ['LoadState', 'ActiveState', 'SubState']
props = get_category_base_props(category)
@ -315,7 +315,7 @@ def validate_unit_and_properties(runner, unit, extra_properties, units_info, pro
if extra_properties:
missing_props = [prop for prop in extra_properties if prop.lower() not in unit_data]
if missing_props:
module.fail_json(msg="The following properties do not exist for unit '{0}': {1}".format(unit, ", ".join(missing_props)))
module.fail_json(msg=f"The following properties do not exist for unit '{unit}': {', '.join(missing_props)}")
return True
@ -333,7 +333,7 @@ def process_wildcards(selected_units, all_units, module):
resolved_units[match] = True
if not resolved_units:
module.fail_json(msg="No units match any of the provided patterns: {}".format(", ".join(non_matching_patterns)))
module.fail_json(msg=f"No units match any of the provided patterns: {', '.join(non_matching_patterns)}")
return resolved_units, non_matching_patterns
@ -346,7 +346,7 @@ def process_unit(runner, unit, extra_properties, units_info, property_cache, sta
category = determine_category(unit)
if not category:
module.fail_json(msg="Could not determine the category for unit '{0}'.".format(unit))
module.fail_json(msg=f"Could not determine the category for unit '{unit}'.")
props = get_category_base_props(category)
full_props = set(props + state_props + extra_properties)

View file

@ -108,7 +108,7 @@ def sysupgrade_run(module):
rc, out, err = module.run_command(cmd + run_flag)
if rc != 0:
module.fail_json(msg="Command %s failed rc=%d, out=%s, err=%s" % (cmd, rc, out, err))
module.fail_json(msg=f"Command {cmd} failed rc={rc}, out={out}, err={err}")
elif out.lower().find('already on latest snapshot') >= 0:
changed = False
elif out.lower().find('upgrade on next reboot') >= 0: