Adds revoke functionality to device license (#48772)
This commit is contained in:
parent
8b8aca21c1
commit
4f80c45c51
1 changed files with 73 additions and 9 deletions
|
@ -39,10 +39,13 @@ options:
|
||||||
- When C(present), only guarantees that a license is there.
|
- When C(present), only guarantees that a license is there.
|
||||||
- When C(latest), ensures that the license is always valid.
|
- When C(latest), ensures that the license is always valid.
|
||||||
- When C(absent), removes the license on the system.
|
- When C(absent), removes the license on the system.
|
||||||
|
- When C(revoked), removes the license on the system and revokes its future usage
|
||||||
|
on the F5 license servers.
|
||||||
default: present
|
default: present
|
||||||
choices:
|
choices:
|
||||||
- absent
|
- absent
|
||||||
- present
|
- present
|
||||||
|
- revoked
|
||||||
accept_eula:
|
accept_eula:
|
||||||
description:
|
description:
|
||||||
- Declares whether you accept the BIG-IP EULA or not. By default, this
|
- Declares whether you accept the BIG-IP EULA or not. By default, this
|
||||||
|
@ -385,6 +388,8 @@ class ModuleManager(object):
|
||||||
changed = self.present()
|
changed = self.present()
|
||||||
elif state == "absent":
|
elif state == "absent":
|
||||||
changed = self.absent()
|
changed = self.absent()
|
||||||
|
elif state == "revoked":
|
||||||
|
changed = self.revoke()
|
||||||
|
|
||||||
reportable = ReportableChanges(params=self.changes.to_return())
|
reportable = ReportableChanges(params=self.changes.to_return())
|
||||||
changes = reportable.to_return()
|
changes = reportable.to_return()
|
||||||
|
@ -402,7 +407,7 @@ class ModuleManager(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
def present(self):
|
def present(self):
|
||||||
if self.exists():
|
if self.exists() and not self.is_revoked():
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self.create()
|
return self.create()
|
||||||
|
@ -415,11 +420,67 @@ class ModuleManager(object):
|
||||||
raise F5ModuleError("Failed to delete the resource.")
|
raise F5ModuleError("Failed to delete the resource.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def revoke(self):
|
||||||
|
if self.is_revoked():
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# When revoking a license, it should be acceptable to auto-accept the
|
||||||
|
# license since you accepted it the first time when you activated the
|
||||||
|
# license you are now revoking.
|
||||||
|
self.want.update({'accept_eula': True})
|
||||||
|
|
||||||
|
# Revoking seems to just be another way of saying "get me a new license".
|
||||||
|
# There appear to be revoke-specific wording in the license and I assume
|
||||||
|
# some special revoke-like signing is happening, but the process is essentially
|
||||||
|
# just another form of "create".
|
||||||
|
return self.create()
|
||||||
|
|
||||||
|
def revoke_from_device(self):
|
||||||
|
if self.module.check_mode:
|
||||||
|
return True
|
||||||
|
|
||||||
|
dossier = self.read_dossier_from_device()
|
||||||
|
if dossier:
|
||||||
|
self.want.update({'dossier': dossier})
|
||||||
|
else:
|
||||||
|
raise F5ModuleError("Dossier not generated.")
|
||||||
|
|
||||||
|
if self.is_revoked():
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_revoked(self):
|
||||||
|
command = '-c "egrep Revoked /config/bigip.license"'
|
||||||
|
params = dict(
|
||||||
|
command='run',
|
||||||
|
utilCmdArgs=command
|
||||||
|
)
|
||||||
|
uri = "https://{0}:{1}/mgmt/tm/util/bash".format(
|
||||||
|
self.client.provider['server'],
|
||||||
|
self.client.provider['server_port']
|
||||||
|
)
|
||||||
|
resp = self.client.api.post(uri, json=params)
|
||||||
|
try:
|
||||||
|
response = resp.json()
|
||||||
|
except ValueError as ex:
|
||||||
|
raise F5ModuleError(str(ex))
|
||||||
|
|
||||||
|
if 'code' in response and response['code'] in [400, 403]:
|
||||||
|
if 'message' in response:
|
||||||
|
raise F5ModuleError(response['message'])
|
||||||
|
else:
|
||||||
|
raise F5ModuleError(resp.content)
|
||||||
|
if 'commandResult' in response and 'Revoked' in response['commandResult']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def read_dossier_from_device(self):
|
def read_dossier_from_device(self):
|
||||||
params = dict(
|
params = dict(
|
||||||
command='run',
|
command='run',
|
||||||
utilCmdArgs='-b "{0}"'.format(self.want.license_key)
|
utilCmdArgs='-b "{0}"'.format(self.want.license_key)
|
||||||
)
|
)
|
||||||
|
if self.want.state == 'revoked':
|
||||||
|
params['utilCmdArgs'] = '-r ' + params['utilCmdArgs']
|
||||||
|
|
||||||
uri = "https://{0}:{1}/mgmt/tm/util/get-dossier".format(
|
uri = "https://{0}:{1}/mgmt/tm/util/get-dossier".format(
|
||||||
self.client.provider['server'],
|
self.client.provider['server'],
|
||||||
self.client.provider['server_port']
|
self.client.provider['server_port']
|
||||||
|
@ -436,7 +497,10 @@ class ModuleManager(object):
|
||||||
else:
|
else:
|
||||||
raise F5ModuleError(resp.content)
|
raise F5ModuleError(resp.content)
|
||||||
try:
|
try:
|
||||||
return response['commandResult']
|
if self.want.state == 'revoked':
|
||||||
|
return response['commandResult'][8:]
|
||||||
|
else:
|
||||||
|
return response['commandResult']
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -455,13 +519,13 @@ class ModuleManager(object):
|
||||||
self.want.license_url,
|
self.want.license_url,
|
||||||
data=self.want.license_envelope,
|
data=self.want.license_envelope,
|
||||||
)
|
)
|
||||||
except Exception as ex:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = LicenseXmlParser(content=resp._content)
|
resp = LicenseXmlParser(content=resp.content)
|
||||||
result = resp.json()
|
result = resp.json()
|
||||||
except F5ModuleError as ex:
|
except F5ModuleError:
|
||||||
# This error occurs when there is a problem with the license server and it
|
# This error occurs when there is a problem with the license server and it
|
||||||
# starts returning invalid XML (like if they upgraded something and the server
|
# starts returning invalid XML (like if they upgraded something and the server
|
||||||
# is redirecting improperly.
|
# is redirecting improperly.
|
||||||
|
@ -469,7 +533,7 @@ class ModuleManager(object):
|
||||||
# There's no way to recover from this error except by notifying F5 that there
|
# There's no way to recover from this error except by notifying F5 that there
|
||||||
# is an issue with the license server.
|
# is an issue with the license server.
|
||||||
raise
|
raise
|
||||||
except Exception as ex:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if result['state'] == 'EULA_REQUIRED':
|
if result['state'] == 'EULA_REQUIRED':
|
||||||
|
@ -554,7 +618,7 @@ class ModuleManager(object):
|
||||||
nops += 1
|
nops += 1
|
||||||
else:
|
else:
|
||||||
nops = 0
|
nops = 0
|
||||||
except Exception as ex:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
|
@ -583,7 +647,7 @@ class ModuleManager(object):
|
||||||
|
|
||||||
if 'commandResult' in response:
|
if 'commandResult' in response:
|
||||||
return True
|
return True
|
||||||
except Exception as ex:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -790,7 +854,7 @@ class ArgumentSpec(object):
|
||||||
default='activate.f5.com'
|
default='activate.f5.com'
|
||||||
),
|
),
|
||||||
state=dict(
|
state=dict(
|
||||||
choices=['absent', 'present'],
|
choices=['absent', 'present', 'revoked'],
|
||||||
default='present'
|
default='present'
|
||||||
),
|
),
|
||||||
accept_eula=dict(
|
accept_eula=dict(
|
||||||
|
|
Loading…
Reference in a new issue