decort_disk remake

rc-5.2.3
msbolshakov 2 years ago
parent e2c9f591b8
commit 8f7c933fb8

@ -157,6 +157,16 @@ options:
- `If specified for an existing disk, and it is greater than current disk size, platform will try to resize
the disk on the fly. Downsizing disk is not allowed.`
required: no
limitIO:
description:
- Disk input / output limit, used to limit the speed of interaction with the disk.
required: no
type:
description:
- Type of the disk.
- `Disks can be of the following types: "D"-Data, "B"-Boot, "T"-Tmp.`
default: "D"
required: no
state:
description:
- Specify the desired state of the disk at the exit of the module.
@ -234,21 +244,131 @@ facts:
gid: 1001
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.decort_utils import *
class decort_disk(DecortController):
def __init__(self,amodule):
super(decort_disk, self).__init__(amodule)
def decort_disk_package_facts(disk_facts, check_mode=False):
"""Package a dictionary of disk facts according to the decort_disk module specification.
This dictionary will be returned to the upstream Ansible engine at the completion of
the module run.
self.validated_account_id = 0
self.validated_disk_id = 0
self.disk_facts = None # will hold Disk facts
self.acc_facts = None # will hold Account facts
# limitIO check for exclusive parameters
if amodule.params['limitIO']:
limit = amodule.params['limitIO']
if limit['total_bytes_sec'] > 0 and limit['read_bytes_sec'] > 0 or limit['write_bytes_sec'] > 0:
self.result['failed'] = True
self.result['msg'] = ("total and read/write of bytes_sec cannot be set at the same time.")
amodule.fail_json(**self.result)
elif limit['total_iops_sec'] > 0 and limit['read_iops_sec'] > 0 or limit['write_iops_sec'] > 0:
self.result['failed'] = True
self.result['msg'] = ("total and read/write of iops_sec cannot be set at the same time.")
amodule.fail_json(**self.result)
elif limit['total_bytes_sec_max'] > 0 and limit['read_bytes_sec_max'] > 0 or limit['write_bytes_sec_max'] > 0:
self.result['failed'] = True
self.result['msg'] = ("total and read/write of bytes_sec_max cannot be set at the same time.")
amodule.fail_json(**self.result)
elif limit['total_iops_sec_max'] > 0 and limit['read_iops_sec_max'] > 0 or limit['write_iops_sec_max'] > 0:
self.result['failed'] = True
self.result['msg'] = ("total and read/write of iops_sec_max cannot be set at the same time.")
amodule.fail_json(**self.result)
if amodule.params['account_id']:
self.validated_account_id = amodule.params['account_id']
elif amodule.params['account_name']:
self.validated_account_id, _ = self.account_find(amodule.params['account_name'])
elif not amodule.params['id'] and not amodule.params['account_name']:
self.result['failed'] = True
self.result['msg'] = ("Cannot found disk without account id or name.")
amodule.fail_json(**self.result)
if self.validated_account_id == 0 and not amodule.params['id']:
# we failed either to find or access the specified account - fail the module
self.result['failed'] = True
self.result['msg'] = ("Cannot find account '{}'").format(amodule.params['account_name'])
amodule.fail_json(**self.result)
if amodule.params['id'] or amodule.params['name']:
self.validated_disk_id, self.disk_facts = self.decort_disk_find(amodule)
else:
self.result['failed'] = True
self.result['msg'] = ("Cannot find or create disk without disk name or disk id")
amodule.fail_json(**self.result)
def decort_disk_create(self, amodule):
if self.disk_facts['status'] in ["DESTROYED", "PURGED"]:
if not amodule.params['limitIO']:
amodule.params['limitIO'] = self.disk_facts['iotune']
self.disk_id = self.disk_create(accountId=self.validated_account_id, gid=self.disk_facts['gid'],
name=self.disk_facts['name'], description=self.disk_facts['desc'],
size=self.disk_facts['sizeMax'], type=self.disk_facts['type'],
iops=self.disk_facts['iotune']['total_iops_sec'],
sep_id=self.disk_facts['sepId'], pool=self.disk_facts['pool'])
self.disk_facts['iotune'] = 0
else:
self.disk_id = self.disk_create(accountId=self.validated_account_id, gid=amodule.params['gid'],
name=amodule.params['name'], description=amodule.params['description'],
size=amodule.params['size'], type=amodule.params['type'],
iops=amodule.params['iops'],
sep_id=amodule.params['sep_id'], pool=amodule.params['pool'])
self.result['failed'] = False
self.result['changed'] = True
self.result['msg'] = ("Disk with id '{}' successfully created.").format(self.disk_id)
return self.disk_id
def decort_disk_delete(self, amodule):
self.disk_id = self.disk_delete(disk_id=self.validated_disk_id,
detach=amodule.params['force_detach'],
permanently=amodule.params['permanently'],
reason=amodule.params['reason'])
return
@param (dict) disk_facts: dictionary with Disk facts as returned by API call to .../disks/get
@param (bool) check_mode: boolean that tells if this Ansible module is run in check mode
"""
def decort_disk_find(self, amodule):
if amodule.params['id']:
self.disk_id, self.disk_facts = self.disk_find(disk_id=amodule.params['id'],
name=amodule.params['name'],
account_id=0)
elif amodule.params['name']:
self.disk_id, self.disk_facts = self.disk_find(disk_id=self.validated_disk_id,
name=amodule.params['name'],
account_id=self.validated_account_id)
if not self.disk_id and not amodule.params['name']:
self.result['failed'] = True
self.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['id'])
amodule.fail_json(**self.result)
self.result['facts'] = decort_disk.decort_disk_package_facts(self.disk_facts)
return self.disk_id, self.disk_facts
def decort_disk_limitIO(self, amodule):
self.limits = amodule.params['limitIO']
self.disk_limitIO(limits = self.limits,
diskId = self.validated_disk_id)
self.disk_facts['iotune'] = amodule.params['limitIO']
self.result['facts'] = decort_disk.decort_disk_package_facts(self.disk_facts)
return
def decort_disk_rename(self, amodule):
self.disk_rename(diskId = self.validated_disk_id,
name = amodule.params['name'])
self.disk_facts['name'] = amodule.params['name']
self.result['facts'] = decort_disk.decort_disk_package_facts(self.disk_facts)
self.result['msg'] = ("Disk with id '{}',successfully renamed to '{}'.").format(self.validated_disk_id, amodule.params['name'])
return
def decort_disk_package_facts(disk_facts, check_mode=False):
ret_dict = dict(id=0,
name="none",
state="CHECK_MODE",
@ -278,6 +398,7 @@ def decort_disk_package_facts(disk_facts, check_mode=False):
ret_dict['pool'] = disk_facts['pool']
ret_dict['attached_to'] = disk_facts['vmid']
ret_dict['gid'] = disk_facts['gid']
ret_dict['iotune'] = disk_facts['iotune']
return ret_dict
@ -314,10 +435,35 @@ def decort_disk_parameters():
required=False,
fallback=(env_fallback, ['DECORT_PASSWORD']),
no_log=True),
place_with=dict(type='int', required=False, default=0),
pool=dict(type='str', required=False, default=''),
sep_id=dict(type='int', required=False, default=0),
size=dict(type='int', required=False),
place_with=dict(type='int', default=0),
pool=dict(type='str', default=''),
sep_id=dict(type='int', default=0),
gid=dict(type='int', default=0),
size=dict(type='int', default=0),
type=dict(type='str',
required=False,
default="D",
choices=['B', 'D', 'T']),
iops=dict(type='int', default=2000),
limitIO=dict(type='dict',
options=dict(
total_bytes_sec=dict(default=0,type='int'),
read_bytes_sec=dict(default=0,type='int'),
write_bytes_sec=dict(default=0,type='int'),
total_iops_sec=dict(default=0,type='int'),
read_iops_sec=dict(default=0,type='int'),
write_iops_sec=dict(default=0,type='int'),
total_bytes_sec_max=dict(default=0,type='int'),
read_bytes_sec_max=dict(default=0,type='int'),
write_bytes_sec_max=dict(default=0,type='int'),
total_iops_sec_max=dict(default=0,type='int'),
read_iops_sec_max=dict(default=0,type='int'),
write_iops_sec_max=dict(default=0,type='int'),
size_iops_sec=dict(default=0,type='int'),)),
permanently=dict(type='bool', required=False, default=False),
reason=dict(type='int', required=False),
description=dict(type='str', required=False,
default="Disk created with Ansible Decort_disk module."),
state=dict(type='str',
default='present',
choices=['absent', 'present']),
@ -330,7 +476,7 @@ def decort_disk_parameters():
)
def main():
module_parameters = decort_disk_parameters()
module_parameters = decort_disk.decort_disk_parameters()
amodule = AnsibleModule(argument_spec=module_parameters,
supports_check_mode=True,
@ -345,197 +491,103 @@ def main():
],
)
decon = DecortController(amodule)
decon = decort_disk(amodule)
disk_id = 0
disk_facts = None # will hold Disk facts
validated_acc_id = 0
acc_facts = None # will hold Account facts
if decon.validated_disk_id == 0 and amodule.params['state'] == 'present':
# if id cannot cannot be found and have a state 'present', then create a new disk
decon.validated_disk_id = decon.decort_disk_create(amodule)
_, decon.disk_facts = decon.decort_disk_find(amodule)
decon.result['changed'] = True
decon.result['msg'] = ("Disk with id '{}' successfully created.").format(decon.validated_disk_id)
if amodule.params['id']:
# expect existing Disk with the specified ID
# This call to disk_find will abort the module if no Disk with such ID is present
disk_id, disk_facts = decon.disk_find(amodule.params['id'])
if not disk_id:
decon.result['failed'] = True
decon.result['msg'] = "Specified Disk ID {} not found.".format(amodule.params['id'])
amodule.fail_json(**decon.result)
validated_acc_id =disk_facts['accountId']
elif amodule.params['account_id'] > 0 or amodule.params['account_name'] != "":
# Make sure disk name is specified, if not - fail the module
if amodule.params['name'] == "":
decon.result['failed'] = True
decon.result['msg'] = ("Cannot manage disk if both ID is 0 and disk name is empty.")
amodule.fail_json(**decon.result)
# Specified account must be present and accessible by the user, otherwise abort the module
validated_acc_id, acc_facts = decon.account_find(amodule.params['account_name'], amodule.params['account_id'])
if not validated_acc_id:
decon.result['failed'] = True
decon.result['msg'] = ("Current user does not have access to the requested account "
"or non-existent account specified.")
amodule.fail_json(**decon.result)
# This call to disk_find may return disk_id=0 if no Disk with this name found in
disk_id, disk_facts = decon.disk_find(disk_id=0, disk_name=amodule.params['name'],
account_id=validated_acc_id,
check_state=False)
else:
# this is "invalid arguments combination" sink
# if we end up here, it means that module was invoked with disk_id=0 and undefined account
decon.result['failed'] = True
if amodule.params['account_id'] == 0 and amodule.params['account_name'] == "":
decon.result['msg'] = "Cannot find Disk by name when account name is empty and account ID is 0."
if amodule.params['name'] == "":
decon.result['msg'] = "Cannot find Disk by empty name."
amodule.fail_json(**decon.result)
elif decon.validated_disk_id == 0 and amodule.params['state'] == 'absent' and amodule.params['name']:
# if disk with specified name cannot be found and have a state 'absent', then nothing to do,
# specified disk already deleted
decon.result['msg'] = ("Disk with name '{}' has already been deleted or your account does not have"
"access to it.")\
.format(amodule.params['name'])
amodule.exit_json(**decon.result)
#
# Initial validation of module arguments is complete
#
# At this point non-zero disk_id means that we will be managing pre-existing Disk
# Otherwise we are about to create a new disk
#
# Valid Disk model statii are as follows:
#
# "CREATED", "ASSIGNED", DELETED", "DESTROYED", "PURGED"
#
elif decon.validated_disk_id == 0 and amodule.params['state'] == 'absent' and amodule.params['id']:
# if disk with specified id cannot be found and have a state 'absent', then nothing to do,
# specified disk already deleted
decon.result['msg'] = ("Disk with name '{}' has already been deleted or your account does not have"
"access to it.")\
.format(decon.validated_disk_id)
amodule.exit_json(**decon.result)
disk_should_exist = False
target_sep_id = 0
# target_pool = ""
elif decon.disk_facts['status'] == "CREATED":
if amodule.params['state'] == 'present':
# if disk status in condition "CREATED" and state "present", nothing to do,
# specified disk already created
decon.result['msg'] = "Specified Disk ID {} already created.".format(decon.validated_disk_id)
if disk_id:
disk_should_exist = True
if disk_facts['status'] in ["MODELED", "CREATING" ]:
# error: nothing can be done to existing Disk in the listed statii regardless of
# the requested state
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("No change can be done for existing Disk ID {} because of its current "
"status '{}'").format(disk_id, disk_facts['status'])
elif disk_facts['status'] in ["CREATED", "ASSIGNED"]:
if amodule.params['state'] == 'absent':
decon.disk_delete(disk_id, True, amodule.params['force_detach']) # delete permanently
disk_facts['status'] = 'DESTROYED'
disk_should_exist = False
elif amodule.params['state'] == 'present':
# resize Disk as necessary & if possible
if decon.check_amodule_argument('size', False):
decon.disk_resize(disk_facts, amodule.params['size'])
elif disk_facts['status'] == "DELETED":
# if disk status in condition "CREATED" and state "absent", delete the disk
decon.validated_disk_id = decon.decort_disk_delete(amodule)
decon.disk_facts['status'] = "DESTROYED"
decon.result['msg'] = ("Disk with id '{}' successfully deleted.").format(decon.disk_facts['id'])
decon.result['facts'] = decon.decort_disk_package_facts(decon.disk_facts)
amodule.exit_json(**decon.result)
elif decon.disk_facts['status'] in ["MODELED", "CREATING" ]:
# if disk in status "MODELED" or "CREATING",
# then we cannot do anything, while disk in this status
decon.result['changed'] = False
decon.result['msg'] = ("Cannot do anything with disk id '{}',please wait until disk will be created.")\
.format(decon.validated_disk_id)
amodule.fail_json(**decon.result)
elif decon.disk_facts['status'] == "DELETED":
if amodule.params['state'] == 'present':
# restore
decon.disk_restore(disk_id)
_, disk_facts = decon.disk_find(disk_id)
decon.disk_resize(disk_facts, amodule.params['size'])
disk_should_exist = True
# if disk in "DELETED" status and "present" state, restore
decon.disk_restore(decon.validated_disk_id)
_, decon.disk_facts = decon.decort_disk_find(amodule)
decon.result['changed'] = True
decon.result['msg'] = ("Disk with id '{}',restored successfully.").format(decon.validated_disk_id)
elif amodule.params['state'] == 'absent':
# destroy permanently
decon.disk_delete(disk_id, permanently=True)
disk_facts['status'] = 'DESTROYED'
disk_should_exist = False
elif disk_facts['status'] in ["DESTROYED", "PURGED"]:
# if disk in "DELETED" status and "absent" state, nothing to do
decon.result['msg'] = "Specified Disk ID {} already destroyed.".format(decon.validated_disk_id)
amodule.exit_json(**decon.result)
elif decon.disk_facts['status'] in ["DESTROYED", "PURGED"]:
if amodule.params['state'] == 'present':
# Need to re-provision this Disk.
# Some attributes may change, some must stay the same:
# - disk name - stays, take from disk_facts
# - account ID - stays, take from validated account ID
# - size - may change, take from module arguments
# - SEP ID - may change, build based on module arguments
# - pool - may change, take from module arguments
# - annotation - may change, take from module arguments
#
# First validate required parameters:
decon.check_amodule_argument('size') # this will fail the module if size is not specified
target_sep_id = 0
if decon.check_amodule_argument('sep_id', False) and amodule.params['sep_id'] > 0:
# non-zero sep_id is explicitly passed in module arguments
target_sep_id = amodule.params['sep_id']
elif decon.check_amodule_argument('place_with', False) and amodule.params['place_with'] > 0:
# request to place this disk on the same SEP as the specified OS image
# validate specified OS image and assign SEP ID accordingly
image_id, image_facts = decon.image_find(amodule.params['place_with'], "", 0)
target_sep_id = image_facts['sepId']
else:
# no new SEP ID is explicitly specified, and no place_with option - use sepId from the disk_facts
target_sep_id = disk_facts['sepId']
disk_id = decon.disk_provision(disk_name=disk_facts['name'], # as this disk was found, its name is in the facts
size=amodule.params['size'],
account_id=validated_acc_id,
sep_id=target_sep_id,
pool=amodule.params['pool'],
desc=amodule.params['annotation'],
location="")
disk_should_exist = True
decon.validated_disk_id = decon.decort_disk_create(amodule)
_, decon.disk_facts = decon.decort_disk_find(amodule)
decon.result['changed'] = True
decon.result['facts'] = decon.decort_disk_package_facts(decon.disk_facts)
decon.result['msg'] = ("Disk with id '{}',created successfully.").format(decon.validated_disk_id)
elif amodule.params['state'] == 'absent':
# nop
decon.result['failed'] = False
decon.result['changed'] = False
decon.result['msg'] = ("No state change required for Disk ID {} because of its "
"current status '{}'").format(disk_id,
disk_facts['status'])
disk_should_exist = False
else:
# disk_id =0 -> pre-existing Disk was not found.
disk_should_exist = False # we will change it back to True if Disk is created successfully
# If requested state is 'absent' - nothing to do
if amodule.params['state'] == 'absent':
decon.result['failed'] = False
decon.result['changed'] = False
decon.result['msg'] = ("Nothing to do as target state 'absent' was requested for "
"non-existent Disk name '{}'").format(amodule.params['name'])
elif amodule.params['state'] == 'present':
decon.check_amodule_argument('name') # if disk name not specified, fail the module
decon.check_amodule_argument('size') # if disk size not specified, fail the module
# as we already have account ID, we can create Disk and get disk id on success
if decon.check_amodule_argument('sep_id', False) and amodule.params['sep_id'] > 0:
# non-zero sep_id is explicitly passed in module arguments
target_sep_id = amodule.params['sep_id']
elif decon.check_amodule_argument('place_with', False) and amodule.params['place_with'] > 0:
# request to place this disk on the same SEP as the specified OS image
# validate specified OS image and assign SEP ID accordingly
image_id, image_facts = decon.image_find(amodule.params['place_with'], "", 0)
target_sep_id = image_facts['sepId']
else:
# no SEP ID is explicitly specified, and no place_with option - we do not know where
# to place the new disk - fail the module
decon.result['msg'] = "Specified Disk ID {} already destroyed.".format(decon.validated_disk_id)
amodule.exit_json(**decon.result)
if amodule.params['state'] == "present":
if decon.disk_facts['sizeMax'] != amodule.params['size']:
if decon.disk_facts['sizeMax'] > amodule.params['size'] and amodule.params['size'] != 0:
decon.result['failed'] = True
decon.result['msg'] = ("Cannot create new Disk name '{}': no SEP ID specified and "
"no 'place_with' option used.").format(amodule.params['name'])
decon.result['msg'] = ("Disk id '{}', cannot reduce disk size.").format(decon.validated_disk_id)
amodule.fail_json(**decon.result)
elif decon.disk_facts['sizeMax'] < amodule.params['size']:
decon.disk_resize(disk_facts=decon.disk_facts,
new_size=amodule.params['size'])
decon.result['changed'] = True
decon.result['msg'] = ("Disk with id '{}',resized successfully.").format(decon.validated_disk_id)
disk_id = decon.disk_provision(disk_name=amodule.params['name'],
size=amodule.params['size'],
account_id=validated_acc_id,
sep_id=target_sep_id,
pool_name=amodule.params['pool'],
desc=amodule.params['annotation'],
location="")
disk_should_exist = True
elif amodule.params['state'] == 'disabled':
decon.result['failed'] = True
decon.result['changed'] = False
decon.result['msg'] = ("Invalid target state '{}' requested for non-existent "
"Disk name '{}'").format(amodule.params['state'],
amodule.params['name'])
if amodule.params['limitIO'] and amodule.params['limitIO'] != decon.disk_facts['iotune']:
decon.decort_disk_limitIO(amodule)
decon.result['changed'] = True
decon.result['msg'] = ("Disk with id '{}',limited successfully.").format(decon.validated_disk_id)
#
# conditional switch end - complete module run
#
if decon.result['failed']:
amodule.fail_json(**decon.result)
else:
# prepare Disk facts to be returned as part of decon.result and then call exit_json(...)
if disk_should_exist:
if decon.result['changed']:
# If we arrive here, there is a good chance that the Disk is present - get fresh Disk
# facts by Disk ID.
# Otherwise, Disk facts from previous call (when the Disk was still in existence) will
# be returned.
_, disk_facts = decon.disk_find(disk_id)
decon.result['facts'] = decort_disk_package_facts(disk_facts, amodule.check_mode)
amodule.exit_json(**decon.result)
if amodule.params['name'] and amodule.params['id']:
if amodule.params['name'] != decon.disk_facts['name']:
decon.decort_disk_rename(amodule)
decon.result['changed'] = True
decon.result['msg'] = ("Disk with id '{}',renamed successfully from '{}' to '{}'.")\
.format(decon.validated_disk_id, decon.disk_facts['name'], amodule.params['name'])
amodule.exit_json(**decon.result)
if __name__ == "__main__":
main()

@ -785,7 +785,7 @@ class DecortController(object):
Note: when Ansible is run in check mode method will return 0.
"""
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "kvmvm_provision")
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "kvmvm_provision2")
if self.amodule.check_mode:
self.result['failed'] = False
@ -2051,8 +2051,27 @@ class DecortController(object):
"response {}.").format(vins_id, api_resp.status_code, api_resp.reason)
return ret_vins_id, ret_vins_dict
def _rg_listvins(self,rg_id):
"""List all ViNS in the resource group
@param (int) rg_id: id onr resource group
"""
if not rg_id:
self.result['failed'] = True
self.result['msg'] = "_rg_listvins(): zero RG ID specified."
self.amodule.fail_json(**self.result)
api_params = dict(rgId=rg_id, )
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/listVins", api_params)
if api_resp.status_code == 200:
ret_rg_vins_list = json.loads(api_resp.content.decode('utf8'))
else:
self.result['warning'] = ("rg_listvins(): failed to get RG by ID {}. HTTP code {}, "
"response {}.").format(rg_id, api_resp.status_code, api_resp.reason)
return []
def vins_find(self, vins_id, vins_name="", account_id=0, rg_id=0, check_state=True):
return ret_rg_vins_list
def vins_find(self, vins_id, vins_name="", account_id=0, rg_id=0, rg_facts="", check_state=True):
"""Find specified ViNS.
@param (int) vins_id: ID of the ViNS. If non-zero vins_id is specified, all other arguments
@ -2093,29 +2112,29 @@ class DecortController(object):
elif vins_name != "":
if rg_id > 0:
# search for ViNS at RG level
validated_id, validated_facts = self._rg_get_by_id(rg_id)
if not validated_id:
self.result['failed'] = True
self.result['msg'] = "vins_find(): cannot find RG ID {}.".format(rg_id)
self.amodule.fail_json(**self.result)
# NOTE: RG's 'vins' attribute does not list destroyed ViNSes!
for runner in validated_facts['vins']:
# api_params['vinsId'] = runner
ret_vins_id, ret_vins_facts = self._vins_get_by_id(runner)
if ret_vins_id and ret_vins_facts['name'] == vins_name:
# validated_id, validated_facts = self._rg_get_by_id(rg_id)
# if not validated_id:
# self.result['failed'] = True
# self.result['msg'] = "vins_find(): cannot find RG ID {}.".format(rg_id)
# self.amodule.fail_json(**self.result)
# # NOTE: RG's 'vins' attribute does not list destroyed ViNSes!
list_vins = self._rg_listvins(rg_id)
for vins in list_vins:
if vins['name'] == vins_name:
ret_vins_id, ret_vins_facts = self._vins_get_by_id(vins['id'])
if not check_state or ret_vins_facts['status'] not in VINS_INVALID_STATES:
return ret_vins_id, ret_vins_facts
else:
return 0, None
elif account_id > 0:
# search for ViNS at account level
validated_id, validated_facts = self.account_find("", account_id)
if not validated_id:
self.result['failed'] = True
self.result['msg'] = "vins_find(): cannot find Account ID {}.".format(account_id)
self.amodule.fail_json(**self.result)
# validated_id, validated_facts = self.account_find("", account_id)
# if not validated_id:
# self.result['failed'] = True
# self.result['msg'] = "vins_find(): cannot find Account ID {}.".format(account_id)
# self.amodule.fail_json(**self.result)
# NOTE: account's 'vins' attribute does not list destroyed ViNSes!
for runner in validated_facts['vins']:
for runner in rg_facts['vins']:
# api_params['vinsId'] = runner
ret_vins_id, ret_vins_facts = self._vins_get_by_id(runner)
if ret_vins_id and ret_vins_facts['name'] == vins_name:
@ -2296,7 +2315,7 @@ class DecortController(object):
desired_state)
return
def vins_update(self, vins_dict, ext_net_id, ext_ip_addr=""):
def vins_update_extnet(self, vins_dict, ext_net_id, ext_ip_addr=""):
"""Update ViNS. Currently only updates to the external network connection settings and
external IP address assignment are implemented.
Note that as ViNS created at account level cannot have external connections, attempt
@ -2318,7 +2337,7 @@ class DecortController(object):
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("vins_update() in check mode: updating ViNS ID {}, name '{}' "
self.result['msg'] = ("vins_update_extnet() in check mode: updating ViNS ID {}, name '{}' "
"was requested.").format(vins_dict['id'], vins_dict['name'])
return
@ -2373,10 +2392,16 @@ class DecortController(object):
gw_config[
'ext_net_id'])
return
def vins_update_mgmt(self, vins_dict, mgmtaddr=""):
def vins_update_mgmt(self, vins_dict, mgmtaddr=[]):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_update_mgmt")
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("vins_update_mgmt() in check mode: updating ViNS ID {}, name '{}' "
"was requested.").format(vins_dict['id'], vins_dict['name'])
return
if self.amodule.params['config_save'] and vins_dict['VNFDev']['customPrecfg']:
# only save config,no other modifictaion
self.result['changed'] = True
@ -2386,13 +2411,12 @@ class DecortController(object):
return
for iface in vins_dict['VNFDev']['interfaces']:
if iface['ipAddress'] == mgmtaddr:
if not iface['listenSsh']:
self._vins_vnf_addmgmtaddr(vins_dict['VNFDev']['id'],mgmtaddr)
if iface['ipAddress'] in mgmtaddr and not iface['listenSsh']:
self._vins_vnf_addmgmtaddr(vins_dict['VNFDev']['id'],iface['ipAddress'])
self.result['changed'] = True
self.result['failed'] = False
elif mgmtaddr =="":
if iface['listenSsh'] and iface['name'] != "ens9":
elif iface['ipAddress'] not in mgmtaddr and iface['listenSsh']:
if iface['name'] != "ens9":
self._vins_vnf_delmgmtaddr(vins_dict['VNFDev']['id'],iface['ipAddress'])
self.result['changed'] = True
self.result['failed'] = False
@ -2411,13 +2435,15 @@ class DecortController(object):
def vins_update_ifaces(self,vins_dict,vinses=""):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_update_ifaces")
existed_conn_ip = []
#vnf_dict = self._get_vnf_by_id(vins_dict['VNFDev']['id'])
list_account_vins = self._get_all_account_vinses(vins_dict['VNFDev']['accountId'])
list_account_vinsid = [rec['id'] for rec in list_account_vins]
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = ("vins_update_iface() in check mode: updating ViNS ID {}, name '{}' "
"was requested.").format(vins_dict['id'], vins_dict['name'])
return
list_ifaces_ip = [rec['ipaddr'] for rec in vinses]
vins_inner = [rec['id'] for rec in vinses]
vins_outer = [rec['id'] for rec in list_account_vins]
vinsid_not_existed = []
for iface in vins_dict['VNFDev']['interfaces']:
if iface['connType'] == "VXLAN" and iface['type'] == "CUSTOM":
if iface['ipAddress'] not in list_ifaces_ip:
@ -2425,19 +2451,30 @@ class DecortController(object):
self.result['changed'] = True
self.result['failed'] = False
else:
existed_conn_ip.append(iface['ipAddress'])
#existed_conn_ip.append(iface['ipAddress'])
vinses = list(filter(lambda i: i['ipaddr']!=iface['ipAddress'],vinses))
if not vinses:
return
list_account_vins = self._get_all_account_vinses(vins_dict['VNFDev']['accountId'])
list_account_vinsid = [rec['id'] for rec in list_account_vins]
for vins in vinses:
if vins['id'] in list_account_vinsid:
_,v_dict = self._vins_get_by_id(vins['id'])
if vins['ipaddr'] not in existed_conn_ip:
#TODO: vins reservation
self._vnf_iface_add(vins_dict['VNFDev']['id'],v_dict['vxlanId'],vins['ipaddr'],vins['netmask'])
self.result['changed'] = True
self.result['failed'] = False
else:
vinsid_not_existed.append(vins['id'])
if vinsid_not_existed:
self.result['warning'] = ("List ViNS id: {} that not created on account id: {}").format(
vinsid_not_existed,
vins_dict['VNFDev']['accountId']
)
return
def _vnf_iface_add(self,arg_devid,arg_vxlanid,arg_ipaddr,arg_netmask="24",arg_defgw=""):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "_vnf_iface_add")
api_params = dict(
devId=arg_devid,
ifType="CUSTOM",
@ -2473,7 +2510,6 @@ class DecortController(object):
return ret_vnf_dict
def _get_all_account_vinses(self,acc_id):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "get_all_account_vinses")
api_params = dict(accountId=acc_id)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/account/listVins", api_params)
if api_resp.status_code == 200:
@ -2484,8 +2520,6 @@ class DecortController(object):
return ret_listvins_dict
def _vins_vnf_addmgmtaddr(self,dev_id,mgmtip):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_vnf_addmgmtaddr")
api_params = dict(devId=dev_id,ip=mgmtip)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudbroker/vnfdev/addMgmtAddr", api_params)
if api_resp.status_code == 200:
@ -2497,8 +2531,6 @@ class DecortController(object):
return
def _vins_vnf_delmgmtaddr(self,dev_id,mgmtip):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_vnf_delmgmtaddr")
api_params = dict(devId=dev_id,ip=mgmtip)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudbroker/vnfdev/delMgmtAddr", api_params)
if api_resp.status_code == 200:
@ -2510,7 +2542,6 @@ class DecortController(object):
return
def _vins_vnf_customconfig_set(self,dev_id,arg_mode=True):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_vnf_customconfig_set")
api_params = dict(devId=dev_id,mode=arg_mode)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudbroker/vnfdev/customSet", api_params)
if api_resp.status_code == 200:
@ -2522,7 +2553,6 @@ class DecortController(object):
return
def _vins_vnf_config_save(self,dev_id):
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "vins_vnf_config_save")
api_params = dict(devId=dev_id)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudbroker/vnfdev/configSave", api_params)
if api_resp.status_code == 200:
@ -2533,19 +2563,13 @@ class DecortController(object):
"response {}.").format(dev_id,api_resp.status_code, api_resp.reason)
return
def vins_vnf_ifaceadd(self):
return
def vins_vnf_ifaceremove(self):
return
##############################
#
# Disk management
#
##############################
def disk_delete(self, disk_id, permanently=False, force_detach=False):
def disk_delete(self, disk_id, permanently, detach, reason):
"""Deletes specified Disk.
@param (int) disk_id: ID of the Disk to be deleted.
@ -2562,8 +2586,9 @@ class DecortController(object):
return
api_params = dict(diskId=disk_id,
detach=force_detach,
permanently=permanently, )
detach=detach,
permanently=permanently,
reason=reason)
self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/delete", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['failed'] = False
@ -2602,7 +2627,7 @@ class DecortController(object):
return ret_disk_id, ret_disk_dict
def disk_find(self, disk_id, disk_name="", account_id=0, check_state=False):
def disk_find(self, disk_id, name, account_id, check_state=False):
"""Find specified Disk.
@param (int) disk_id: ID of the Disk. If non-zero disk_id is specified, all other arguments
@ -2638,11 +2663,11 @@ class DecortController(object):
self.result['failed'] = True
self.result['msg'] = "disk_find(): cannot find Disk by ID {}.".format(disk_id)
self.amodule.fail_json(**self.result)
if not check_state or ret_disk_facts['status'] not in DISK_INVALID_STATES:
if not check_state or ret_disk_facts['status']:
return ret_disk_id, ret_disk_facts
else:
return 0, None
elif disk_name != "":
elif name != "":
if account_id > 0:
api_params = dict(accountId=account_id)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/list", api_params)
@ -2650,15 +2675,15 @@ class DecortController(object):
disks_list = json.loads(api_resp.content.decode('utf8'))
for runner in disks_list:
# return the first disk of the specified name that fulfills status matching rule
if runner['name'] == disk_name:
if not check_state or runner['status'] not in DISK_INVALID_STATES:
if runner['name'] == name:
if not check_state or runner['status']:
return runner['id'], runner
else:
return 0, None
else: # we are missing meaningful account_id - fail the module
self.result['failed'] = True
self.result['msg'] = ("disk_find(): cannot find Disk by name '{}' "
"when no account ID specified.").format(disk_name)
"when no account ID specified.").format(name)
self.amodule.fail_json(**self.result)
else: # Disk ID is 0 and Disk name is emtpy - fail the module
self.result['failed'] = True
@ -2667,55 +2692,40 @@ class DecortController(object):
return 0, None
def disk_provision(self, disk_name, size, account_id, sep_id, pool_name="", desc="", location=""):
def disk_create(self, accountId, gid, name, description, size, type, iops, sep_id, pool):
"""Provision Disk according to the specified arguments.
Note that disks created by this method will be of type 'D' (data disks).
If critical error occurs the embedded call to API function will abort further execution
of the script and relay error to Ansible.
@param (string) disk_name: name to assign to the Disk.
@param (string) name: name to assign to the Disk.
@param (int) size: size of the disk in GB.
@param (int) account_id: ID of the account where disk will belong.
@param (int) accountId: ID of the account where disk will belong.
@param (int) sep_id: ID of the SEP (Storage Endpoint Provider), where disk will be created.
@param (string) pool: optional name of the pool, where this disk will be created.
@param (string) desc: optional text description of this disk.
@param (string) location: optional location, where disk resources will be provided. If empty
string is specified the disk will be created in the default location under DECORT controller.
@param (string) description: optional text description of this disk.
@param (int) gid: optional Grid id, if specified the disk will be created in selected
location.
@return: ID of the newly created Disk (in Ansible check mode 0 is returned).
"""
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_provision")
if self.amodule.check_mode:
self.result['failed'] = False
self.result['msg'] = "disk_provision() in check mode: create Disk name '{}' was requested.".format(
disk_name)
return 0
target_gid = self.gid_get(location)
if not target_gid:
self.result['failed'] = True
self.result['msg'] = "disk_provision() failed to obtain Grid ID for default location."
self.amodule.fail_json(**self.result)
ret_disk_id = 0
api_params = dict(accountId=account_id,
gid=target_gid,
name=disk_name,
desc=desc,
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_creation")
api_params = dict(accountId=accountId,
gid=gid,
name=name,
description=description,
size=size,
type='D',
sepId=sep_id, )
if pool_name != "":
api_params['pool'] = pool_name
type=type,
iops=iops,
sepId=sep_id,
pool=pool )
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/create", api_params)
if api_resp.status_code == 200:
ret_disk_id = json.loads(api_resp.content.decode('utf8'))
self.result['failed'] = False
self.result['changed'] = True
return ret_disk_id
def disk_resize(self, disk_facts, new_size):
@ -2770,6 +2780,48 @@ class DecortController(object):
return
def disk_limitIO(self, limits, diskId):
"""Limits already created Disk identified by its ID.
@param (dict) limits: Dictionary with limits.
@param (int) diskId: ID of the Disk to limit.
@returns: nothing on success. On error this method will abort module execution.
"""
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_limitIO")
api_params = dict(diskId=diskId,
total_bytes_sec=limits['total_bytes_sec'],
read_bytes_sec=limits['read_bytes_sec'],
write_bytes_sec=limits['write_bytes_sec'],
total_iops_sec=limits['total_iops_sec'],
read_iops_sec=limits['read_iops_sec'],
write_iops_sec=limits['write_iops_sec'],
total_bytes_sec_max=limits['total_bytes_sec_max'],
read_bytes_sec_max=limits['read_bytes_sec_max'],
write_bytes_sec_max=limits['write_bytes_sec_max'],
total_iops_sec_max=limits['total_iops_sec_max'],
read_iops_sec_max=limits['read_iops_sec_max'],
write_iops_sec_max=limits['write_iops_sec_max'],
size_iops_sec=limits['size_iops_sec'])
self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/limitIO", api_params)
self.result['msg'] = "Specified Disk ID {} limited successfully.".format(self.validated_disk_id)
return
def disk_rename(self, diskId, name):
"""Renames disk to the specified new name.
@param disk_id: ID of the Disk to rename.
@param name: New name.
@returns: nothing on success. On error this method will abort module execution.
"""
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_rename")
api_params = dict(diskId=diskId,
name=name)
self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/rename", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
self.result['failed'] = False
self.result['changed'] = True
return
def disk_restore(self, disk_id):
"""Restores previously deleted Disk identified by its ID. For restore to succeed
the Disk must be in 'DELETED' state.
@ -3146,7 +3198,7 @@ class DecortController(object):
return
api_params = dict(k8sId=k8s_id,
permanently=False,
permanently=permanently,
)
self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/delete", api_params)
# On success the above call will return here. On error it will abort execution by calling fail_json.
@ -3238,9 +3290,11 @@ class DecortController(object):
ret_info = json.loads(api_get_resp.content.decode('utf8'))
if api_get_resp.status_code == 200:
if ret_info['status'] in ["PROCESSING", "SCHEDULED"]:
self.result['msg'] = ("k8s_provision(): Can't create cluster")
self.result['failed'] = False
time.sleep(30)
elif ret_info['status'] == "ERROR":
self.result['msg'] = ("k8s_provision(): Can't create cluster")
self.result['failed'] = True
return
elif ret_info['status'] == "OK":
@ -3250,10 +3304,13 @@ class DecortController(object):
else:
k8s_id = ret_info['status']
else:
self.result['msg'] = ("k8s_provision(): Can't create cluster")
self.result['failed'] = True
# Timeout
self.result['msg'] = ("k8s_provision(): Can't create cluster")
self.result['failed'] = True
else:
self.result['msg'] = ("k8s_provision(): Can't create cluster")
self.result['failed'] = True
return
@ -3283,11 +3340,12 @@ class DecortController(object):
for rec_inn in arg_k8swg['k8sGroups']['workers']:
for rec_out in arg_modwg:
if rec_inn['name'] == rec_out['name']:
if rec_inn['num'] != rec_out['num'] and rec_out['num'] != 0:
count = rec_inn['num']-rec_out['num']
cmp_list = []
if count > 0:
for cmp in rec_inn['detailedInfo'][:count]:
for cmp in rec_inn['detailedInfo'][-count:]:
cmp_list.append(cmp['id'])
wg_moddel_list.append({rec_inn['id']:cmp_list})
if count < 0:
@ -3335,15 +3393,17 @@ class DecortController(object):
api_params = dict(includeDisabled=False)
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8ci/list", api_params)
k8ci_id_present = False
if api_resp.status_code == 200:
ret_k8ci_list = json.loads(api_resp.content.decode('utf8'))
for k8ci_item in ret_k8ci_list:
if k8ci_item['id'] == arg_k8ci_id:
k8ci_id_present = True
break
else:
if k8ci_id_present == False:
self.result['failed'] = True
self.result['msg'] = "k8s_k8ci_find(): cannot find ID."
self.result['msg'] = ("Cannot find k8ci id: {}.").format(arg_k8ci_id)
self.amodule.fail_json(**self.result)
else:
self.result['failed'] = True

Loading…
Cancel
Save