Debug and fix for disk and KVM VM handling

master
Sergey Shubin svs1370 5 years ago
parent e5edf40b6e
commit 9a2c909961

@ -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

@ -556,6 +556,9 @@ class decort_kvmvm(DecortController):
userdata=cloud_init_params, userdata=cloud_init_params,
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:

Loading…
Cancel
Save