Debug and fix for disk and KVM VM handling
This commit is contained in:
@@ -361,7 +361,7 @@ def main():
|
|||||||
decon.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['id'])
|
decon.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['id'])
|
||||||
amodule.fail_json(**decon.result)
|
amodule.fail_json(**decon.result)
|
||||||
validated_acc_id =disk_facts['accountId']
|
validated_acc_id =disk_facts['accountId']
|
||||||
elif (amodule.params['account_id'] or amodule.params['account_name'] != "") and amodule.params['name'] != "":
|
elif amodule.params['account_id'] > 0 or amodule.params['account_name'] != "":
|
||||||
# Make sure disk name is specified, if not - fail the module
|
# Make sure disk name is specified, if not - fail the module
|
||||||
if amodule.params['name'] == "":
|
if amodule.params['name'] == "":
|
||||||
decon.result['failed'] = True
|
decon.result['failed'] = True
|
||||||
|
|||||||
@@ -557,6 +557,9 @@ class decort_kvmvm(DecortController):
|
|||||||
start_on_create=start_compute)
|
start_on_create=start_compute)
|
||||||
self.comp_should_exist = True
|
self.comp_should_exist = True
|
||||||
|
|
||||||
|
# Need to re-read comp_info after VM was provisioned
|
||||||
|
_, self.comp_info, _ = self.compute_find(self.comp_id)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Compute was created
|
# Compute was created
|
||||||
#
|
#
|
||||||
@@ -595,6 +598,8 @@ class decort_kvmvm(DecortController):
|
|||||||
"""Compute modify handler for KVM VM management by decort_kvmvm module.
|
"""Compute modify handler for KVM VM management by decort_kvmvm module.
|
||||||
This method is a convenience wrapper that calls individual Compute modification functions from
|
This method is a convenience wrapper that calls individual Compute modification functions from
|
||||||
DECORT utility library (module).
|
DECORT utility library (module).
|
||||||
|
|
||||||
|
Note that it does not modify power state of KVM VM.
|
||||||
"""
|
"""
|
||||||
self.compute_networks(self.comp_info, self.amodule.params['networks'])
|
self.compute_networks(self.comp_info, self.amodule.params['networks'])
|
||||||
self.compute_bootdisk_size(self.comp_info, self.amodule.params['boot_disk'])
|
self.compute_bootdisk_size(self.comp_info, self.amodule.params['boot_disk'])
|
||||||
@@ -674,7 +679,7 @@ class decort_kvmvm(DecortController):
|
|||||||
for ddisk in self.comp_info['disks']:
|
for ddisk in self.comp_info['disks']:
|
||||||
if ddisk['type'] == 'B':
|
if ddisk['type'] == 'B':
|
||||||
# if it is a boot disk - store its size
|
# if it is a boot disk - store its size
|
||||||
ret_dict['disk_size'] = ddisk['maxSize']
|
ret_dict['disk_size'] = ddisk['sizeMax']
|
||||||
elif ddisk['type'] == 'D':
|
elif ddisk['type'] == 'D':
|
||||||
# if it is a data disk - append its ID to the list of data disks IDs
|
# if it is a data disk - append its ID to the list of data disks IDs
|
||||||
ret_dict['data_disks'].append(ddisk['id'])
|
ret_dict['data_disks'].append(ddisk['id'])
|
||||||
@@ -804,27 +809,15 @@ def main():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if subj.comp_id:
|
if subj.comp_id:
|
||||||
if subj.comp_info['status'] in ("MIGRATING", "DESTROYING", "ERROR"):
|
if subj.comp_info['status'] in ("DISABLED", "MIGRATING", "DELETING", "DESTROYING", "ERROR", "REDEPLOYING"):
|
||||||
# nothing to do for an existing Compute in the listed states regardless of the requested state
|
# cannot do anything on the existing Compute in the listed states
|
||||||
subj.nop()
|
subj.error() # was subj.nop()
|
||||||
elif subj.comp_info['status'] == "RUNNING":
|
elif subj.comp_info['status'] == "ENABLED":
|
||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
subj.destroy()
|
subj.destroy()
|
||||||
elif amodule.params['state'] in ('present', 'poweredon'):
|
elif amodule.params['state'] in ('present', 'paused', 'poweredon', 'poweredoff', 'halted'):
|
||||||
# check port forwards / check size / nop
|
|
||||||
subj.modify()
|
|
||||||
elif amodule.params['state'] in ('paused', 'poweredoff', 'halted'):
|
|
||||||
# pause or power off the vm, then check port forwards / check size
|
|
||||||
subj.compute_powerstate(subj.comp_info, amodule.params['state'])
|
subj.compute_powerstate(subj.comp_info, amodule.params['state'])
|
||||||
subj.modify(arg_wait_cycles=7)
|
subj.modify(arg_wait_cycles=7)
|
||||||
elif subj.comp_info['status'] in ("PAUSED", "HALTED"):
|
|
||||||
if amodule.params['state'] == 'absent':
|
|
||||||
subj.destroy()
|
|
||||||
elif amodule.params['state'] in ('present', 'paused', 'poweredoff', 'halted'):
|
|
||||||
subj.modify()
|
|
||||||
elif amodule.params['state'] == 'poweredon':
|
|
||||||
subj.modify()
|
|
||||||
subj.compute_powerstate(subj.comp_info, amodule.params['state'])
|
|
||||||
elif subj.comp_info['status'] == "DELETED":
|
elif subj.comp_info['status'] == "DELETED":
|
||||||
if amodule.params['state'] in ('present', 'poweredon'):
|
if amodule.params['state'] in ('present', 'poweredon'):
|
||||||
# TODO - check if restore API returns VM ID (similarly to VM create API)
|
# TODO - check if restore API returns VM ID (similarly to VM create API)
|
||||||
@@ -839,9 +832,7 @@ def main():
|
|||||||
subj.error()
|
subj.error()
|
||||||
elif subj.comp_info['status'] == "DESTROYED":
|
elif subj.comp_info['status'] == "DESTROYED":
|
||||||
if amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
if amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||||
subj.create()
|
subj.create() # this call will also handle data disk & network connection
|
||||||
# TODO: Network setup?
|
|
||||||
# TODO: Data disks setup?
|
|
||||||
elif amodule.params['state'] == 'absent':
|
elif amodule.params['state'] == 'absent':
|
||||||
subj.nop()
|
subj.nop()
|
||||||
subj.comp_should_exist = False
|
subj.comp_should_exist = False
|
||||||
@@ -853,9 +844,7 @@ def main():
|
|||||||
if amodule.params['state'] == 'absent':
|
if amodule.params['state'] == 'absent':
|
||||||
subj.nop()
|
subj.nop()
|
||||||
elif amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
elif amodule.params['state'] in ('present', 'poweredon', 'poweredoff', 'halted'):
|
||||||
subj.create()
|
subj.create() # this call will also handle data disk & network connection
|
||||||
# TODO: Network setup?
|
|
||||||
# TODO: Data disks setup?
|
|
||||||
elif amodule.params['state'] == 'paused':
|
elif amodule.params['state'] == 'paused':
|
||||||
subj.error()
|
subj.error()
|
||||||
|
|
||||||
@@ -872,8 +861,8 @@ def main():
|
|||||||
# TODO: check if we really need to get RG facts here in view of DNF implementation
|
# TODO: check if we really need to get RG facts here in view of DNF implementation
|
||||||
# we need to extract RG facts regardless of 'changed' flag, as it is our source of information on
|
# we need to extract RG facts regardless of 'changed' flag, as it is our source of information on
|
||||||
# the VDC external IP address
|
# the VDC external IP address
|
||||||
_, rg_facts = subj.rg_find(arg_rg_id=subj.rg_id)
|
_, rg_facts = subj.rg_find(arg_account_id=0, arg_rg_id=subj.rg_id)
|
||||||
subj.result['facts'] = subj.package_facts(rg_facts, amodule.check_mode)
|
subj.result['facts'] = subj.package_facts(amodule.check_mode)
|
||||||
amodule.exit_json(**subj.result)
|
amodule.exit_json(**subj.result)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -661,7 +661,8 @@ class DecortController(object):
|
|||||||
# @param wait_for_change: integer number that tells how many 5 seconds intervals to wait for the power state
|
# @param wait_for_change: integer number that tells how many 5 seconds intervals to wait for the power state
|
||||||
# change before returning from this method.
|
# change before returning from this method.
|
||||||
|
|
||||||
NOP_STATES_FOR_POWER_CHANGE = ["MIGRATING", "DELETED", "DESTROYING", "DESTROYED", "ERROR"]
|
INVALID_MODEL_STATES = ["MIGRATING", "DELETED", "DESTROYING", "DESTROYED", "ERROR", "REDEPLOYING"]
|
||||||
|
INVALID_TECH_STATES = ["STOPPING", "STARTING", "PAUSING"]
|
||||||
|
|
||||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "compute_powerstate")
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "compute_powerstate")
|
||||||
|
|
||||||
@@ -671,48 +672,51 @@ class DecortController(object):
|
|||||||
"to '{}' was requested.").format(comp_facts['id'], target_state)
|
"to '{}' was requested.").format(comp_facts['id'], target_state)
|
||||||
return
|
return
|
||||||
|
|
||||||
if comp_facts['status'] in NOP_STATES_FOR_POWER_CHANGE:
|
if comp_facts['status'] in INVALID_MODEL_STATES:
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['msg'] = ("compute_powerstate(): no power state change possible for Compute ID {} "
|
self.result['msg'] = ("compute_powerstate(): no power state change possible for Compute ID {} "
|
||||||
"in its current state '{}'.").format(comp_facts['id'], comp_facts['status'])
|
"in its current state '{}'.").format(comp_facts['id'], comp_facts['status'])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if comp_facts['techStatus'] in INVALID_TECH_STATES:
|
||||||
|
self.result['failed'] = False
|
||||||
|
self.result['msg'] = ("compute_powerstate(): no power state change possible for Compute ID {} "
|
||||||
|
"in its current techState '{}'.").format(comp_facts['id'], comp_facts['techStatus'])
|
||||||
|
return
|
||||||
|
|
||||||
powerstate_api = "" # this string will also be used as a flag to indicate that API call is necessary
|
powerstate_api = "" # this string will also be used as a flag to indicate that API call is necessary
|
||||||
api_params = dict(compId=comp_facts['id'])
|
api_params = dict(compId=comp_facts['id'])
|
||||||
expected_state = ""
|
expected_state = ""
|
||||||
|
|
||||||
if comp_facts['status'] == "RUNNING":
|
if comp_facts['techStatus'] == "STARTED":
|
||||||
if target_state == 'paused':
|
if target_state == 'paused':
|
||||||
powerstate_api = "/restmachine/cloudapi/compute/pause"
|
powerstate_api = "/restmachine/cloudapi/compute/pause"
|
||||||
expected_state = "PAUSED"
|
expected_techState = "PAUSED"
|
||||||
elif target_state in ('poweredoff', 'halted', 'stopped'):
|
elif target_state in ('poweredoff', 'halted', 'stopped'):
|
||||||
powerstate_api = "/restmachine/cloudapi/compute/stop"
|
powerstate_api = "/restmachine/cloudapi/compute/stop"
|
||||||
params['force'] = force_change
|
params['force'] = force_change
|
||||||
expected_state = "HALTED"
|
expected_techState = "STOPPED"
|
||||||
elif target_state == 'restarted':
|
elif target_state == 'restarted':
|
||||||
powerstate_api = "/restmachine/cloudapi/compute/reboot"
|
powerstate_api = "/restmachine/cloudapi/compute/reboot"
|
||||||
expected_state = "RUNNING"
|
expected_techState = "STARTED"
|
||||||
elif comp_facts['status'] == "PAUSED" and target_state in ('poweredon', 'restarted', 'started'):
|
elif comp_facts['techStatus'] == "PAUSED" and target_state in ('poweredon', 'restarted', 'started'):
|
||||||
powerstate_api = "/restmachine/cloudapi/compute/resume"
|
powerstate_api = "/restmachine/cloudapi/compute/resume"
|
||||||
expected_state = "RUNNING"
|
expected_techState = "STARTED"
|
||||||
elif comp_facts['status'] == "HALTED" and target_state in ('poweredon', 'restarted', 'started'):
|
elif comp_facts['techStatus'] == "STOPPED" and target_state in ('poweredon', 'restarted', 'started'):
|
||||||
powerstate_api = "/restmachine/cloudapi/compute/start"
|
powerstate_api = "/restmachine/cloudapi/compute/start"
|
||||||
expected_state = "RUNNING"
|
expected_techState = "STARTED"
|
||||||
else:
|
|
||||||
# This Compute instance seems to be in the desired power state already - do not call API
|
|
||||||
pass
|
|
||||||
|
|
||||||
if powerstate_api != "":
|
if powerstate_api != "":
|
||||||
self.decort_api_call(requests.post, powerstate_api, api_params)
|
self.decort_api_call(requests.post, powerstate_api, api_params)
|
||||||
# On success the above call will return here. On error it will abort execution by calling fail_json.
|
# On success the above call will return here. On error it will abort execution by calling fail_json.
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['changed'] = True
|
self.result['changed'] = True
|
||||||
comp_facts['status'] = expected_state
|
comp_facts['techStatus'] = expected_techState
|
||||||
else:
|
else:
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['warning'] = ("compute_powerstate(): no power state change required for Compute ID {} from its "
|
self.result['warning'] = ("compute_powerstate(): no power state change required for Compute ID {} from its "
|
||||||
"current state '{}' to desired state '{}'.").format(comp_facts['id'],
|
"current state '{}' to desired state '{}'.").format(comp_facts['id'],
|
||||||
comp_facts['status'],
|
comp_facts['techStatus'],
|
||||||
target_state)
|
target_state)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -766,7 +770,8 @@ class DecortController(object):
|
|||||||
cpu=cpu, ram=ram,
|
cpu=cpu, ram=ram,
|
||||||
imageId=image_id,
|
imageId=image_id,
|
||||||
bootDisk=boot_disk,
|
bootDisk=boot_disk,
|
||||||
start=start_on_create) # start_machine parameter requires DECORT API ver 3.3.1 or higher
|
start=start_on_create, # start_machine parameter requires DECORT API ver 3.3.1 or higher
|
||||||
|
netType="NONE") # we create VM without any network connections
|
||||||
if userdata:
|
if userdata:
|
||||||
api_params['userdata'] = json.dumps(userdata) # we need to pass a string object as "userdata"
|
api_params['userdata'] = json.dumps(userdata) # we need to pass a string object as "userdata"
|
||||||
|
|
||||||
@@ -816,7 +821,7 @@ class DecortController(object):
|
|||||||
"Compute ID {}.").format(comp_dict['accountId'], comp_dict['id'])
|
"Compute ID {}.").format(comp_dict['accountId'], comp_dict['id'])
|
||||||
return
|
return
|
||||||
|
|
||||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/externalnetworks/list", api_params)
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/externalnetwork/list", api_params)
|
||||||
extnet_list = json.loads(api_resp.content.decode('utf8')) # list of dicts: "name" holds "NET_ADDR/NETMASK", "id" is ID
|
extnet_list = json.loads(api_resp.content.decode('utf8')) # list of dicts: "name" holds "NET_ADDR/NETMASK", "id" is ID
|
||||||
if not len(vins_list):
|
if not len(vins_list):
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
@@ -2217,7 +2222,7 @@ class DecortController(object):
|
|||||||
api_params = dict(accountId=account_id,
|
api_params = dict(accountId=account_id,
|
||||||
name=disk_name,
|
name=disk_name,
|
||||||
showAll=False) # we do not want to see disks in DESTROYED, PURGED or invalid states
|
showAll=False) # we do not want to see disks in DESTROYED, PURGED or invalid states
|
||||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/list", api_params)
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/search", api_params)
|
||||||
# the above call may return more than one matching disk
|
# the above call may return more than one matching disk
|
||||||
disks_list = json.loads(api_resp.content.decode('utf8'))
|
disks_list = json.loads(api_resp.content.decode('utf8'))
|
||||||
for runner in disks_list:
|
for runner in disks_list:
|
||||||
|
|||||||
Reference in New Issue
Block a user