demo update
This commit is contained in:
@@ -192,6 +192,9 @@ options:
|
|||||||
- If I(ssh_key) is not specified, this parameter is ignored and a warning is generated.
|
- If I(ssh_key) is not specified, this parameter is ignored and a warning is generated.
|
||||||
- This parameter is valid at VM creation time only and ignored for any operation on existing VMs.
|
- This parameter is valid at VM creation time only and ignored for any operation on existing VMs.
|
||||||
required: no
|
required: no
|
||||||
|
user_data:
|
||||||
|
description:
|
||||||
|
- Cloud-init User-Data, exept ssh module
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Specify the desired state of the virtual machine at the exit of the module.
|
- Specify the desired state of the virtual machine at the exit of the module.
|
||||||
@@ -548,15 +551,18 @@ class decort_kvmvm(DecortController):
|
|||||||
if self.amodule.params['state'] in ('halted', 'poweredoff'):
|
if self.amodule.params['state'] in ('halted', 'poweredoff'):
|
||||||
start_compute = False
|
start_compute = False
|
||||||
|
|
||||||
if self.amodule.params['ssh_key'] and self.amodule.params['ssh_key_user']:
|
if self.amodule.params['ssh_key'] and self.amodule.params['ssh_key_user'] and not self.amodule.params['ci_user_data']:
|
||||||
cloud_init_params = {'users': [
|
cloud_init_params = {'users': [
|
||||||
{"name": self.amodule.params['ssh_key_user'],
|
{"name": self.amodule.params['ssh_key_user'],
|
||||||
"ssh-authorized-keys": [self.amodule.params['ssh_key']],
|
"ssh-authorized-keys": [self.amodule.params['ssh_key']],
|
||||||
"shell": '/bin/bash'}
|
"shell": '/bin/bash'}
|
||||||
]}
|
]}
|
||||||
|
elif self.amodule.params['ci_user_data']:
|
||||||
|
cloud_init_params = {}
|
||||||
|
for ci_param in self.amodule.params['ci_user_data']:
|
||||||
|
cloud_init_params.update(ci_param)
|
||||||
else:
|
else:
|
||||||
cloud_init_params = None
|
cloud_init_params = None
|
||||||
|
|
||||||
# if we get through here, all parameters required to create new Compute instance should be at hand
|
# if we get through here, all parameters required to create new Compute instance should be at hand
|
||||||
|
|
||||||
# NOTE: KVM VM is created in HALTED state and must be explicitly started
|
# NOTE: KVM VM is created in HALTED state and must be explicitly started
|
||||||
@@ -595,6 +601,11 @@ class decort_kvmvm(DecortController):
|
|||||||
# Next manage data disks
|
# Next manage data disks
|
||||||
self.compute_data_disks(self.comp_info, self.amodule.params['data_disks'])
|
self.compute_data_disks(self.comp_info, self.amodule.params['data_disks'])
|
||||||
|
|
||||||
|
self.compute_affinity(self.comp_info,
|
||||||
|
self.amodule.params['tag'],
|
||||||
|
self.amodule.params['aff_rule'],
|
||||||
|
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
|
# NOTE: see NOTE above regarding libvirt "feature" and new VMs created in HALTED state
|
||||||
if self.amodule.params['state'] not in ('halted', 'poweredoff'):
|
if self.amodule.params['state'] not in ('halted', 'poweredoff'):
|
||||||
self.compute_powerstate(self.comp_info, 'started')
|
self.compute_powerstate(self.comp_info, 'started')
|
||||||
@@ -641,6 +652,11 @@ class decort_kvmvm(DecortController):
|
|||||||
self.compute_resize(self.comp_info,
|
self.compute_resize(self.comp_info,
|
||||||
self.amodule.params['cpu'], self.amodule.params['ram'],
|
self.amodule.params['cpu'], self.amodule.params['ram'],
|
||||||
wait_for_state_change=arg_wait_cycles)
|
wait_for_state_change=arg_wait_cycles)
|
||||||
|
self.compute_affinity(self.comp_info,
|
||||||
|
self.amodule.params['tag'],
|
||||||
|
self.amodule.params['aff_rule'],
|
||||||
|
self.amodule.params['aaff_rule'],
|
||||||
|
label=self.amodule.params['affinity_label'],)
|
||||||
return
|
return
|
||||||
|
|
||||||
def package_facts(self, check_mode=False):
|
def package_facts(self, check_mode=False):
|
||||||
@@ -774,6 +790,11 @@ class decort_kvmvm(DecortController):
|
|||||||
rg_name=dict(type='str', default=""),
|
rg_name=dict(type='str', default=""),
|
||||||
ssh_key=dict(type='str', required=False),
|
ssh_key=dict(type='str', required=False),
|
||||||
ssh_key_user=dict(type='str', required=False),
|
ssh_key_user=dict(type='str', required=False),
|
||||||
|
tag=dict(type='list', 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='list', required=False),
|
||||||
state=dict(type='str',
|
state=dict(type='str',
|
||||||
default='present',
|
default='present',
|
||||||
choices=['absent', 'paused', 'poweredoff', 'halted', 'poweredon', 'present', 'check']),
|
choices=['absent', 'paused', 'poweredoff', 'halted', 'poweredon', 'present', 'check']),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ NOTE: this utility library requires DECORT platform version 3.4.0 or higher.
|
|||||||
It is not compatible with older versions.
|
It is not compatible with older versions.
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- python >= 2.6
|
- python >= 3.8
|
||||||
- PyJWT Python module
|
- PyJWT Python module
|
||||||
- requests Python module
|
- requests Python module
|
||||||
- netaddr Python module
|
- netaddr Python module
|
||||||
@@ -1189,6 +1189,45 @@ class DecortController(object):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def compute_affinity(self,comp_dict,tags,aff,aaff,label=""):
|
||||||
|
|
||||||
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "compute_affinity")
|
||||||
|
|
||||||
|
api_params = dict(computeId=comp_dict['id'])
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/affinityRulesClear", api_params)
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/antiAffinityRulesClear", api_params)
|
||||||
|
if tags:
|
||||||
|
for tag in tags:
|
||||||
|
api_params = dict(computeId=comp_dict['id'],
|
||||||
|
key=tag['key'],
|
||||||
|
value=tag['value'], )
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/tagAdd", api_params)
|
||||||
|
if label:
|
||||||
|
api_params = dict(computeId=comp_dict['id'],
|
||||||
|
affinityLabel=label,)
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/affinityLabelSet", api_params)
|
||||||
|
if len(aff[0])>0:
|
||||||
|
for rule in aff:
|
||||||
|
api_params = dict(computeId=comp_dict['id'],
|
||||||
|
key=rule['key'],
|
||||||
|
value=rule['value'],
|
||||||
|
topology=rule['topology'],
|
||||||
|
mode=rule['mode'],
|
||||||
|
policy=rule['policy'],)
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/affinityRuleAdd", api_params)
|
||||||
|
if len(aaff[0])>0:
|
||||||
|
for rule in aaff:
|
||||||
|
api_params = dict(computeId=comp_dict['id'],
|
||||||
|
key=rule['key'],
|
||||||
|
value=rule['value'],
|
||||||
|
topology=rule['topology'],
|
||||||
|
mode=rule['mode'],
|
||||||
|
policy=rule['policy'],)
|
||||||
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/antiAffinityRuleAdd", api_params)
|
||||||
|
|
||||||
|
self.result['failed'] = False
|
||||||
|
self.result['changed'] = True
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# OS image manipulation methods
|
# OS image manipulation methods
|
||||||
###################################
|
###################################
|
||||||
@@ -2678,7 +2717,12 @@ class DecortController(object):
|
|||||||
|
|
||||||
return ret_k8s_id, ret_k8s_dict
|
return ret_k8s_id, ret_k8s_dict
|
||||||
|
|
||||||
def k8s_find(self, arg_k8s_id=0, arg_k8s_name="", arg_check_state=True):
|
##############################
|
||||||
|
#
|
||||||
|
# K8s management
|
||||||
|
#
|
||||||
|
##############################
|
||||||
|
def k8s_find(self, k8s_id, k8s_name="",rg_id=0,check_state=True):
|
||||||
"""Returns non zero k8s ID and a dictionary with k8s details on success, 0 and empty dictionary otherwise.
|
"""Returns non zero k8s ID and a dictionary with k8s details on success, 0 and empty dictionary otherwise.
|
||||||
This method does not fail the run if k8s cannot be located by its name (arg_k8s_name), because this could be
|
This method does not fail the run if k8s cannot be located by its name (arg_k8s_name), because this could be
|
||||||
an indicator of the requested k8s never existed before.
|
an indicator of the requested k8s never existed before.
|
||||||
@@ -2700,7 +2744,7 @@ class DecortController(object):
|
|||||||
# Transient state (ending with ING) are invalid from k8s manipulation viewpoint
|
# Transient state (ending with ING) are invalid from k8s manipulation viewpoint
|
||||||
#
|
#
|
||||||
|
|
||||||
K8S_INVALID_STATES = ["MODELED"]
|
K8S_INVALID_STATES = ["MODELED","DESTROYED","DESTROYING"]
|
||||||
|
|
||||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_find")
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_find")
|
||||||
|
|
||||||
@@ -2708,31 +2752,22 @@ class DecortController(object):
|
|||||||
api_params = dict(includedeleted=True)
|
api_params = dict(includedeleted=True)
|
||||||
ret_k8s_dict = None
|
ret_k8s_dict = None
|
||||||
|
|
||||||
if arg_k8s_id > 0:
|
if k8s_id:
|
||||||
ret_k8s_id, ret_k8s_dict = self._k8s_get_by_id(arg_k8s_id)
|
ret_k8s_id, ret_k8s_dict = self._k8s_get_by_id(k8s_id)
|
||||||
if not ret_k8s_id:
|
if not ret_k8s_id:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
self.result['msg'] = "k8s_find(): cannot find k8s by ID {}.".format(arg_k8s_id)
|
self.result['msg'] = "k8s_find(): cannot find k8s cluster by ID {}.".format(k8s_id)
|
||||||
self.amodule.fail_json(**self.result)
|
self.amodule.fail_json(**self.result)
|
||||||
elif arg_k8s_name != "":
|
else:
|
||||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/list", api_params)
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/list", api_params)
|
||||||
if api_resp.status_code == 200:
|
if api_resp.status_code == 200:
|
||||||
account_specs = json.loads(api_resp.content.decode('utf8'))
|
k8s_list = json.loads(api_resp.content.decode('utf8'))
|
||||||
for k8s_item in account_specs:
|
for k8s_item in k8s_list:
|
||||||
got_id, got_specs = self._k8s_get_by_id(k8s_item['id'])
|
if k8s_item['name'] == k8s_name and k8s_item['rgId'] == rg_id:
|
||||||
if got_id and got_specs['name'] == arg_k8s_name:
|
if not check_state or k8s_item['status'] not in K8S_INVALID_STATES:
|
||||||
# name matches
|
ret_k8s_id = k8s_item['id']
|
||||||
if not arg_check_state or got_specs['status'] not in K8S_INVALID_STATES:
|
_, ret_k8s_dict = self._k8s_get_by_id(ret_k8s_id)
|
||||||
ret_k8s_id = got_id
|
|
||||||
ret_k8s_dict = got_specs
|
|
||||||
break
|
|
||||||
# Note: we do not fail the run if k8s cannot be located by its name, because it could be a new k8s
|
|
||||||
# that never existed before. In this case ret_k8s_id=0 and empty ret_k8s_dict will be returned.
|
|
||||||
else:
|
|
||||||
# Both arg_k8s_id and arg_k8s_name are empty - there is no way to locate k8s in this case
|
|
||||||
self.result['failed'] = True
|
|
||||||
self.result['msg'] = "k8s_find(): either non-zero ID or a non-empty name must be specified."
|
|
||||||
self.amodule.fail_json(**self.result)
|
|
||||||
|
|
||||||
return ret_k8s_id, ret_k8s_dict
|
return ret_k8s_id, ret_k8s_dict
|
||||||
|
|
||||||
@@ -2772,7 +2807,8 @@ class DecortController(object):
|
|||||||
"'{}' was requested.").format(arg_k8s_dict['id'], arg_k8s_dict['name'],
|
"'{}' was requested.").format(arg_k8s_dict['id'], arg_k8s_dict['name'],
|
||||||
arg_desired_state)
|
arg_desired_state)
|
||||||
return
|
return
|
||||||
|
if arg_desired_state == 'present':
|
||||||
|
arg_desired_state = 'enabled'
|
||||||
k8s_state_api = "" # This string will also be used as a flag to indicate that API call is necessary
|
k8s_state_api = "" # This string will also be used as a flag to indicate that API call is necessary
|
||||||
api_params = dict(k8sId=arg_k8s_dict['id'])
|
api_params = dict(k8sId=arg_k8s_dict['id'])
|
||||||
expected_state = ""
|
expected_state = ""
|
||||||
@@ -2802,23 +2838,26 @@ class DecortController(object):
|
|||||||
"state '{}' to desired state '{}'.").format(arg_k8s_dict['id'],
|
"state '{}' to desired state '{}'.").format(arg_k8s_dict['id'],
|
||||||
arg_k8s_dict['status'],
|
arg_k8s_dict['status'],
|
||||||
arg_desired_state)
|
arg_desired_state)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def k8s_delete(self, k8s_id, permanently=False):
|
def k8s_delete(self, k8s_id, permanently=False):
|
||||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_delete")
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_delete")
|
||||||
|
|
||||||
if self.amodule.check_mode:
|
if self.amodule.check_mode:
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['msg'] = "k8s_delete() in check mode: delete Compute ID {} was requested.".format(k8s_id)
|
self.result['msg'] = "k8s_delete() in check mode: delete K8s cluster ID {} was requested.".format(k8s_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
api_params = dict(k8sId=k8s_id,
|
api_params = dict(k8sId=k8s_id,
|
||||||
permanently=permanently,
|
permanently=False,
|
||||||
)
|
)
|
||||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/delete", api_params)
|
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.
|
# 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
|
||||||
return
|
return
|
||||||
|
|
||||||
def k8s_restore(self, k8s_id ):
|
def k8s_restore(self, k8s_id ):
|
||||||
"""Restores a deleted k8s cluster identified by ID.
|
"""Restores a deleted k8s cluster identified by ID.
|
||||||
|
|
||||||
@@ -2838,6 +2877,16 @@ class DecortController(object):
|
|||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['changed'] = True
|
self.result['changed'] = True
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def k8s_enable(self,k8s_id):
|
||||||
|
|
||||||
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_enable")
|
||||||
|
api_params = dict(k8sId=k8s_id)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/enable", api_params)
|
||||||
|
self.result['failed'] = False
|
||||||
|
self.result['changed'] = True
|
||||||
|
return
|
||||||
|
|
||||||
def k8s_provision(self, k8s_name,
|
def k8s_provision(self, k8s_name,
|
||||||
wg_name, k8ci_id,
|
wg_name, k8ci_id,
|
||||||
rg_id, master_count,
|
rg_id, master_count,
|
||||||
@@ -2902,3 +2951,104 @@ class DecortController(object):
|
|||||||
else:
|
else:
|
||||||
self.result['failed'] = True
|
self.result['failed'] = True
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def k8s_workers_modify(self,arg_k8swg,arg_modwg):
|
||||||
|
|
||||||
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_workers_modify")
|
||||||
|
|
||||||
|
|
||||||
|
if self.k8s_info['techStatus'] != "STARTED":
|
||||||
|
self.result['changed'] = False
|
||||||
|
self.result['msg'] = ("k8s_workers_modify(): Can't modify with TechStatus other then STARTED")
|
||||||
|
return
|
||||||
|
|
||||||
|
wg_del_list = []
|
||||||
|
wg_add_list = []
|
||||||
|
wg_modadd_list = []
|
||||||
|
wg_moddel_list = []
|
||||||
|
wg_outer = [rec['name'] for rec in arg_modwg]
|
||||||
|
wg_inner = [rec['name'] for rec in arg_k8swg['k8sGroups']['workers']]
|
||||||
|
|
||||||
|
for rec in arg_k8swg['k8sGroups']['workers']:
|
||||||
|
if rec['name'] not in wg_outer:
|
||||||
|
wg_del_list.append(rec['id'])
|
||||||
|
for rec in arg_modwg:
|
||||||
|
if rec['name'] not in wg_inner:
|
||||||
|
wg_add_list.append(rec)
|
||||||
|
|
||||||
|
for rec_inn in arg_k8swg['k8sGroups']['workers']:
|
||||||
|
for rec_out in arg_modwg:
|
||||||
|
if rec_inn['num'] != rec_out['num']:
|
||||||
|
count = rec_inn['num']-rec_out['num']
|
||||||
|
cmp_list = []
|
||||||
|
if count > 0:
|
||||||
|
for cmp in rec_inn['detailedInfo'][:count]:
|
||||||
|
cmp_list.append(cmp['id'])
|
||||||
|
wg_moddel_list.append({rec_inn['id']:cmp_list})
|
||||||
|
if count < 0:
|
||||||
|
wg_modadd_list.append({rec_inn['id']:abs(count)})
|
||||||
|
|
||||||
|
if wg_del_list:
|
||||||
|
for wgid in wg_del_list:
|
||||||
|
api_params = dict(k8sId=self.k8s_id,workersGroupId=wgid)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/workersGroupDelete", api_params)
|
||||||
|
self.result['changed'] = True
|
||||||
|
if wg_add_list:
|
||||||
|
for wg in wg_add_list:
|
||||||
|
api_params = dict(k8sId=self.k8s_id,
|
||||||
|
name=wg['name'],
|
||||||
|
workerNum=wg['num'],
|
||||||
|
workerCpu=wg['cpu'],
|
||||||
|
workerRam=wg['ram'],
|
||||||
|
workerDisk=wg['disk'],
|
||||||
|
)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/workersGroupAdd", api_params)
|
||||||
|
self.result['changed'] = True
|
||||||
|
if wg_modadd_list:
|
||||||
|
for wg in wg_modadd_list:
|
||||||
|
for key in wg:
|
||||||
|
api_params = dict(k8sId=self.k8s_id,workersGroupId=key,num=wg[key])
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/workerAdd", api_params)
|
||||||
|
self.result['changed'] = True
|
||||||
|
if wg_moddel_list:
|
||||||
|
for wg in wg_moddel_list:
|
||||||
|
for key in wg:
|
||||||
|
for cmpid in wg[key]:
|
||||||
|
api_params = dict(k8sId=self.k8s_id,workersGroupId=key,workerId=cmpid)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/deleteWorkerFromGroup", api_params)
|
||||||
|
self.result['changed'] = True
|
||||||
|
self.result['failed'] = False
|
||||||
|
return
|
||||||
|
|
||||||
|
def k8s_k8ci_find(self,arg_k8ci_id):
|
||||||
|
|
||||||
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_k8ci_find")
|
||||||
|
|
||||||
|
api_params = dict(includeDisabled=False)
|
||||||
|
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8ci/list", api_params)
|
||||||
|
|
||||||
|
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:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.result['failed'] = True
|
||||||
|
self.result['msg'] = "k8s_k8ci_find(): cannot find ID."
|
||||||
|
self.amodule.fail_json(**self.result)
|
||||||
|
else:
|
||||||
|
self.result['failed'] = True
|
||||||
|
self.result['msg'] = ("Failed to get k8ci list HTTP code {}.").format(api_resp.status_code)
|
||||||
|
self.amodule.fail_json(**self.result)
|
||||||
|
return arg_k8ci_id
|
||||||
|
|
||||||
|
def k8s_getConfig(self):
|
||||||
|
|
||||||
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "k8s_getConfig")
|
||||||
|
|
||||||
|
api_params = dict(k8sId=self.k8s_id)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/k8s/getConfig", api_params)
|
||||||
|
ret_conf = api_resp.content.decode('utf8')
|
||||||
|
return ret_conf
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user