7.0.0
This commit is contained in:
@@ -13,9 +13,10 @@ from ansible.module_utils.decort_utils import *
|
||||
|
||||
|
||||
class decort_kvmvm(DecortController):
|
||||
def __init__(self, arg_amodule):
|
||||
def __init__(self):
|
||||
# call superclass constructor first
|
||||
super(decort_kvmvm, self).__init__(arg_amodule)
|
||||
super(decort_kvmvm, self).__init__(AnsibleModule(**self.amodule_init_args))
|
||||
arg_amodule = self.amodule
|
||||
|
||||
self.check_amodule_args()
|
||||
|
||||
@@ -27,6 +28,7 @@ class decort_kvmvm(DecortController):
|
||||
self.comp_info = None
|
||||
self.acc_id = 0
|
||||
self.rg_id = 0
|
||||
self.aparam_image = None
|
||||
|
||||
validated_acc_id =0
|
||||
validated_rg_id = 0
|
||||
@@ -122,17 +124,36 @@ class decort_kvmvm(DecortController):
|
||||
cannot be implemented using Ansible Argument spec.
|
||||
"""
|
||||
|
||||
# Check parameter "networks" for DPDK type
|
||||
# Check parameter "networks"
|
||||
aparam_nets = self.aparams['networks']
|
||||
if aparam_nets:
|
||||
check_error = False
|
||||
net_types = {net['type'] for net in aparam_nets}
|
||||
DPDK = 'DPDK'
|
||||
if DPDK in net_types and not net_types.issubset({'DPDK', 'EMPTY'}):
|
||||
self.message(
|
||||
'Check for parameter "networks" failed: a compute cannot'
|
||||
' be connected to a DPDK network and a network of another'
|
||||
' type at the same time.'
|
||||
)
|
||||
# DPDK and other networks
|
||||
if self.VMNetType.DPDK.value in net_types:
|
||||
if not net_types.issubset(
|
||||
{self.VMNetType.DPDK.value, self.VMNetType.EMPTY.value}
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
' a compute cannot be connected to a DPDK network and'
|
||||
' a network of another type at the same time.'
|
||||
)
|
||||
# MTU for non-DPDK networks
|
||||
for net in aparam_nets:
|
||||
if (
|
||||
net['type'] != self.VMNetType.DPDK.value
|
||||
and net['mtu'] is not None
|
||||
):
|
||||
check_error = True
|
||||
self.message(
|
||||
'Check for parameter "networks" failed:'
|
||||
' MTU can be specifed only for DPDK network'
|
||||
' (remove parameter "mtu" for network'
|
||||
f' {net["type"]} with ID {net["id"]}).'
|
||||
)
|
||||
if check_error:
|
||||
self.exit(fail=True)
|
||||
|
||||
aparam_custom_fields = self.aparams['custom_fields']
|
||||
@@ -197,17 +218,7 @@ class decort_kvmvm(DecortController):
|
||||
validated_bdisk_size = self.amodule.params['boot_disk'] or 0
|
||||
|
||||
image_id, image_facts = None, None
|
||||
if (
|
||||
self.amodule.params['image_id'] is None
|
||||
and self.amodule.params['image_name'] is None
|
||||
):
|
||||
if self.amodule.params['state'] not in ('poweredoff', 'halted'):
|
||||
self.result['msg'] = (
|
||||
'"state" parameter for a blank Compute must be either '
|
||||
'"poweredoff" or "halted".'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
else:
|
||||
if self.aparam_image:
|
||||
# either image_name or image_id must be present
|
||||
if (
|
||||
self.check_amodule_argument('image_id', abort=False)
|
||||
@@ -242,7 +253,7 @@ class decort_kvmvm(DecortController):
|
||||
#
|
||||
# Once this "feature" is fixed, make sure VM is created according to the actual desired state
|
||||
#
|
||||
start_compute = False # change this once a workaround for the aforementioned libvirt "feature" is implemented
|
||||
start_compute = False # change this once a workaround for the aforementioned libvirt "feature" is implemented
|
||||
if self.amodule.params['state'] in ('halted', 'poweredoff'):
|
||||
start_compute = False
|
||||
|
||||
@@ -331,8 +342,9 @@ class decort_kvmvm(DecortController):
|
||||
self.amodule.params['aaff_rule'],
|
||||
label=self.amodule.params['affinity_label'],)
|
||||
# NOTE: see NOTE above regarding libvirt "feature" and new VMs created in HALTED state
|
||||
if self.amodule.params['state'] not in ('halted', 'poweredoff'):
|
||||
self.compute_powerstate(self.comp_info, 'started')
|
||||
if self.aparam_image:
|
||||
if self.amodule.params['state'] not in ('halted', 'poweredoff'):
|
||||
self.compute_powerstate(self.comp_info, 'started')
|
||||
|
||||
if self.aparams['custom_fields'] is None:
|
||||
custom_fields_disable = True
|
||||
@@ -352,7 +364,13 @@ class decort_kvmvm(DecortController):
|
||||
need_custom_fields=True,
|
||||
)
|
||||
|
||||
self.skip_final_get = True
|
||||
if self.compute_update_args:
|
||||
self.compute_update(
|
||||
compute_id=self.comp_info['id'],
|
||||
**self.compute_update_args,
|
||||
)
|
||||
else:
|
||||
self.skip_final_get = True
|
||||
|
||||
return
|
||||
|
||||
@@ -446,6 +464,7 @@ class decort_kvmvm(DecortController):
|
||||
'hp_backed': 'hpBacked',
|
||||
'numa_affinity': 'numaAffinity',
|
||||
'description': 'desc',
|
||||
'auto_start': 'autoStart',
|
||||
}
|
||||
for param_name, comp_field_name in params_to_check.items():
|
||||
aparam_value = self.amodule.params[param_name]
|
||||
@@ -457,7 +476,6 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
return result_args
|
||||
|
||||
|
||||
def package_facts(self, check_mode=False):
|
||||
"""Package a dictionary of KVM VM facts according to the decort_kvmvm module specification.
|
||||
This dictionary will be returned to the upstream Ansible engine at the completion of decort_kvmvm
|
||||
@@ -492,6 +510,7 @@ class decort_kvmvm(DecortController):
|
||||
hp_backed="",
|
||||
numa_affinity="",
|
||||
custom_fields={},
|
||||
vnc_password="",
|
||||
)
|
||||
|
||||
if check_mode or self.comp_info is None:
|
||||
@@ -554,14 +573,36 @@ class decort_kvmvm(DecortController):
|
||||
|
||||
ret_dict['custom_fields'] = self.comp_info['custom_fields']
|
||||
|
||||
ret_dict['vnc_password'] = self.comp_info['vncPasswd']
|
||||
|
||||
ret_dict['auto_start'] = self.comp_info['autoStart']
|
||||
|
||||
return ret_dict
|
||||
|
||||
def check_amodule_args_for_create(self):
|
||||
# Check for unacceptable parameters for a blank Compute
|
||||
if (
|
||||
self.aparams['image_id'] is None
|
||||
and self.aparams['image_name'] is None
|
||||
self.aparams['image_id'] is not None
|
||||
or self.aparams['image_name'] is not None
|
||||
):
|
||||
self.aparam_image = True
|
||||
else:
|
||||
self.aparam_image = False
|
||||
if (
|
||||
self.aparams['state'] is not None
|
||||
and self.aparams['state'] not in (
|
||||
'present',
|
||||
'poweredoff',
|
||||
'halted',
|
||||
)
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "state" failed: '
|
||||
'state for a blank Compute must be either '
|
||||
'"present", "poweredoff" or "halted".'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
for parameter in (
|
||||
'ssh_key',
|
||||
'ssh_key_user',
|
||||
@@ -586,130 +627,175 @@ class decort_kvmvm(DecortController):
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
@staticmethod
|
||||
def build_parameters():
|
||||
"""Build and return a dictionary of parameters expected by decort_kvmvm module in a form
|
||||
accepted by AnsibleModule utility class.
|
||||
This dictionary is then used y AnsibleModule class instance to parse and validate parameters
|
||||
passed to the module from the playbook.
|
||||
"""
|
||||
|
||||
return dict(
|
||||
account_id=dict(type='int', required=False, default=0),
|
||||
account_name=dict(type='str', required=False, default=''),
|
||||
description=dict(type='str', required=False),
|
||||
app_id=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_APP_ID'])),
|
||||
app_secret=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_APP_SECRET']),
|
||||
no_log=True),
|
||||
authenticator=dict(type='str',
|
||||
required=True,
|
||||
choices=['legacy', 'oauth2', 'jwt']),
|
||||
boot_disk=dict(type='int', required=False),
|
||||
sep_id=dict(type='int', required=False),
|
||||
pool=dict(type='str', required=False),
|
||||
controller_url=dict(type='str', required=True),
|
||||
# count=dict(type='int', required=False, default=1),
|
||||
cpu=dict(type='int', required=False),
|
||||
# datacenter=dict(type='str', required=False, default=''),
|
||||
data_disks=dict(type='list', required=False), # list of integer disk IDs
|
||||
id=dict(type='int', required=False, default=0),
|
||||
image_id=dict(type='int', required=False),
|
||||
image_name=dict(type='str', required=False),
|
||||
jwt=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_JWT']),
|
||||
no_log=True),
|
||||
name=dict(type='str'),
|
||||
networks=dict(
|
||||
type='list',
|
||||
elements='dict',
|
||||
options=dict(
|
||||
type=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=[
|
||||
'VINS',
|
||||
'EXTNET',
|
||||
'VFNIC',
|
||||
'DPDK',
|
||||
'EMPTY',
|
||||
],
|
||||
@property
|
||||
def amodule_init_args(self) -> dict:
|
||||
return self.pack_amodule_init_args(
|
||||
argument_spec=dict(
|
||||
account_id=dict(
|
||||
type='int',
|
||||
default=0,
|
||||
),
|
||||
account_name=dict(
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
description=dict(
|
||||
type='str',
|
||||
),
|
||||
boot_disk=dict(
|
||||
type='int',
|
||||
),
|
||||
sep_id=dict(
|
||||
type='int',
|
||||
),
|
||||
pool=dict(
|
||||
type='str',
|
||||
),
|
||||
controller_url=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
),
|
||||
cpu=dict(
|
||||
type='int',
|
||||
),
|
||||
data_disks=dict( # list of integer disk IDs
|
||||
type='list',
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
default=0,
|
||||
),
|
||||
image_id=dict(
|
||||
type='int',
|
||||
),
|
||||
image_name=dict(
|
||||
type='str',
|
||||
),
|
||||
name=dict(
|
||||
type='str',
|
||||
),
|
||||
networks=dict(
|
||||
type='list',
|
||||
elements='dict',
|
||||
options=dict(
|
||||
type=dict(
|
||||
type='str',
|
||||
required=True,
|
||||
choices=[
|
||||
'VINS',
|
||||
'EXTNET',
|
||||
'VFNIC',
|
||||
'DPDK',
|
||||
'EMPTY',
|
||||
],
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
),
|
||||
ip_addr=dict(
|
||||
type='str',
|
||||
),
|
||||
mtu=dict(
|
||||
type='int',
|
||||
),
|
||||
),
|
||||
id=dict(
|
||||
type='int',
|
||||
),
|
||||
ip_addr=dict(
|
||||
type='str',
|
||||
),
|
||||
),
|
||||
required_if=[
|
||||
('type', 'VINS', ('id',)),
|
||||
('type', 'EXTNET', ('id',)),
|
||||
('type', 'VFNIC', ('id',)),
|
||||
('type', 'DPDK', ('id',)),
|
||||
],
|
||||
),
|
||||
network_order_changing=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
oauth2_url=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_OAUTH2_URL'])),
|
||||
password=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_PASSWORD']),
|
||||
no_log=True),
|
||||
ram=dict(type='int', required=False),
|
||||
rg_id=dict(type='int', default=0),
|
||||
rg_name=dict(type='str', default=""),
|
||||
ssh_key=dict(type='str', required=False),
|
||||
ssh_key_user=dict(type='str', required=False),
|
||||
tag=dict(type='dict', required=False),
|
||||
affinity_label=dict(type='str', required=False),
|
||||
aff_rule=dict(type='list', required=False),
|
||||
aaff_rule=dict(type='list', required=False),
|
||||
ci_user_data=dict(type='dict', required=False),
|
||||
state=dict(type='str',
|
||||
default='present',
|
||||
choices=['absent', 'paused', 'poweredoff', 'halted', 'poweredon', 'present', 'check']),
|
||||
tags=dict(type='str', required=False),
|
||||
user=dict(type='str',
|
||||
required=False,
|
||||
fallback=(env_fallback, ['DECORT_USER'])),
|
||||
verify_ssl=dict(type='bool', required=False, default=True),
|
||||
# wait_for_ip_address=dict(type='bool', required=False, default=False),
|
||||
workflow_callback=dict(type='str', required=False),
|
||||
workflow_context=dict(type='str', required=False),
|
||||
chipset=dict(
|
||||
type='str',
|
||||
choices=['Q35', 'i440fx']
|
||||
),
|
||||
cpu_pin=dict(
|
||||
type='bool',
|
||||
),
|
||||
hp_backed=dict(
|
||||
type='bool',
|
||||
),
|
||||
numa_affinity=dict(
|
||||
type='str',
|
||||
choices=['strict', 'loose', 'none'],
|
||||
),
|
||||
custom_fields=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
fields=dict(
|
||||
type='dict',
|
||||
),
|
||||
disable=dict(
|
||||
type='bool',
|
||||
required_if=[
|
||||
('type', 'VINS', ('id',)),
|
||||
('type', 'EXTNET', ('id',)),
|
||||
('type', 'VFNIC', ('id',)),
|
||||
('type', 'DPDK', ('id',)),
|
||||
],
|
||||
),
|
||||
network_order_changing=dict(
|
||||
type='bool',
|
||||
default=False,
|
||||
),
|
||||
ram=dict(
|
||||
type='int',
|
||||
),
|
||||
rg_id=dict(
|
||||
type='int',
|
||||
default=0,
|
||||
),
|
||||
rg_name=dict(
|
||||
type='str',
|
||||
default='',
|
||||
),
|
||||
ssh_key=dict(
|
||||
type='str',
|
||||
),
|
||||
ssh_key_user=dict(
|
||||
type='str',
|
||||
),
|
||||
tag=dict(
|
||||
type='dict',
|
||||
),
|
||||
affinity_label=dict(
|
||||
type='str',
|
||||
),
|
||||
aff_rule=dict(
|
||||
type='list',
|
||||
),
|
||||
aaff_rule=dict(
|
||||
type='list',
|
||||
),
|
||||
ci_user_data=dict(
|
||||
type='dict',
|
||||
),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'absent',
|
||||
'paused',
|
||||
'poweredoff',
|
||||
'halted',
|
||||
'poweredon',
|
||||
'present',
|
||||
],
|
||||
),
|
||||
tags=dict(
|
||||
type='str',
|
||||
),
|
||||
chipset=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'Q35',
|
||||
'i440fx',
|
||||
]
|
||||
),
|
||||
cpu_pin=dict(
|
||||
type='bool',
|
||||
),
|
||||
hp_backed=dict(
|
||||
type='bool',
|
||||
),
|
||||
numa_affinity=dict(
|
||||
type='str',
|
||||
choices=[
|
||||
'strict',
|
||||
'loose',
|
||||
'none',
|
||||
],
|
||||
),
|
||||
custom_fields=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
fields=dict(
|
||||
type='dict',
|
||||
),
|
||||
disable=dict(
|
||||
type='bool',
|
||||
),
|
||||
),
|
||||
),
|
||||
auto_start=dict(
|
||||
type='bool',
|
||||
)
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_one_of=[
|
||||
('id', 'name'),
|
||||
],
|
||||
)
|
||||
|
||||
def check_amodule_args_for_change(self):
|
||||
@@ -734,6 +820,17 @@ class decort_kvmvm(DecortController):
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
if (
|
||||
not self.comp_info['imageId']
|
||||
and self.amodule.params['state'] in ('poweredon', 'paused')
|
||||
):
|
||||
self.message(
|
||||
'Check for parameter "state" failed: '
|
||||
'state for a blank Compute can not be "poweredon" or "paused".'
|
||||
)
|
||||
self.exit(fail=True)
|
||||
|
||||
|
||||
# Workflow digest:
|
||||
# 1) authenticate to DECORT controller & validate authentication by issuing API call - done when creating DECSController
|
||||
# 2) check if the VM with the specified id or rg_name:name exists
|
||||
@@ -747,49 +844,10 @@ class decort_kvmvm(DecortController):
|
||||
# 6) report result to Ansible
|
||||
|
||||
def main():
|
||||
module_parameters = decort_kvmvm.build_parameters()
|
||||
|
||||
amodule = AnsibleModule(argument_spec=module_parameters,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
['oauth2', 'password'],
|
||||
['password', 'jwt'],
|
||||
['jwt', 'oauth2'],
|
||||
],
|
||||
required_together=[
|
||||
['app_id', 'app_secret'],
|
||||
['user', 'password'],
|
||||
],
|
||||
required_one_of=[
|
||||
['id', 'name'],
|
||||
],
|
||||
)
|
||||
|
||||
# Initialize DECORT KVM VM instance object
|
||||
# This object does not necessarily represent an existing KVM VM
|
||||
subj = decort_kvmvm(amodule)
|
||||
|
||||
# handle state=check before any other logic
|
||||
if amodule.params['state'] == 'check':
|
||||
subj.result['changed'] = False
|
||||
if subj.comp_id:
|
||||
# Compute is found - package facts and report success to Ansible
|
||||
subj.result['failed'] = False
|
||||
# _, subj.comp_info, _ = subj.compute_find(comp_id=subj.comp_id)
|
||||
# _, rg_facts = subj.rg_find(arg_account_id=0, arg_rg_id=subj.rg_id)
|
||||
subj.result['facts'] = subj.package_facts(amodule.check_mode)
|
||||
amodule.exit_json(**subj.result)
|
||||
# we exit the module at this point
|
||||
else:
|
||||
subj.result['failed'] = True
|
||||
subj.result['msg'] = ("Cannot locate Compute name '{}'. Other arguments are: Compute ID {}, "
|
||||
"RG name '{}', RG ID {}, Account '{}'.").format(amodule.params['name'],
|
||||
amodule.params['id'],
|
||||
amodule.params['rg_name'],
|
||||
amodule.params['rg_id'],
|
||||
amodule.params['account_name'])
|
||||
amodule.fail_json(**subj.result)
|
||||
pass
|
||||
subj = decort_kvmvm()
|
||||
amodule = subj.amodule
|
||||
|
||||
if subj.comp_id:
|
||||
subj.check_amodule_args_for_change()
|
||||
@@ -800,8 +858,14 @@ def main():
|
||||
elif subj.comp_info['status'] in ("ENABLED", "DISABLED"):
|
||||
if amodule.params['state'] == 'absent':
|
||||
subj.destroy()
|
||||
elif amodule.params['state'] in ('present', 'paused', 'poweredon', 'poweredoff', 'halted'):
|
||||
subj.compute_powerstate(subj.comp_info, amodule.params['state'])
|
||||
else:
|
||||
if amodule.params['state'] in (
|
||||
'paused', 'poweredon', 'poweredoff', 'halted'
|
||||
):
|
||||
subj.compute_powerstate(
|
||||
comp_facts=subj.comp_info,
|
||||
target_state=amodule.params['state'],
|
||||
)
|
||||
subj.modify(arg_wait_cycles=7)
|
||||
elif subj.comp_info['status'] == "DELETED":
|
||||
if amodule.params['state'] in ('present', 'poweredon'):
|
||||
@@ -830,13 +894,16 @@ def main():
|
||||
else:
|
||||
subj.check_amodule_args_for_create()
|
||||
|
||||
state = amodule.params['state']
|
||||
if state is None:
|
||||
state = 'present'
|
||||
# Preexisting Compute of specified identity was not found.
|
||||
# If requested state is 'absent' - nothing to do
|
||||
if amodule.params['state'] == 'absent':
|
||||
if state == 'absent':
|
||||
subj.nop()
|
||||
elif amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||
elif state in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||
subj.create() # this call will also handle data disk & network connection
|
||||
elif amodule.params['state'] == 'paused':
|
||||
elif state == 'paused':
|
||||
subj.error()
|
||||
|
||||
if subj.result['failed']:
|
||||
|
||||
Reference in New Issue
Block a user