Some impruvments and updates
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
#
|
||||
# Digital Enegry Cloud Orchestration Technology (DECORT) modules for Ansible
|
||||
# Copyright: (c) 2018-2021 Digital Energy Cloud Solutions LLC
|
||||
# Copyright: (c) 2018-2023 Digital Energy Cloud Solutions LLC
|
||||
#
|
||||
# Apache License 2.0 (see http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
#
|
||||
|
||||
#
|
||||
# Author: Sergey Shubin (sergey.shubin@digitalenergy.online)
|
||||
#
|
||||
|
||||
"""
|
||||
This is the library of utility functions and classes for managing DECORT cloud platform.
|
||||
@@ -28,7 +25,7 @@ Requirements:
|
||||
- PyJWT Python module
|
||||
- requests Python module
|
||||
- netaddr Python module
|
||||
- DECORT cloud platform version 3.6.1 or higher
|
||||
- DECORT cloud platform version 3.8.6 or higher
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -39,17 +36,6 @@ import requests
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
#
|
||||
# TODO: the following functionality to be implemented and/or tested
|
||||
# 4) workflow callbacks
|
||||
# 5) run phase states
|
||||
# 6) vm_tags - set/manage VM tags
|
||||
# 7) vm_attributes - change VM attributes (name, annotation) after VM creation - do we need this in Ansible?
|
||||
# 9) test vm_restore() method and execution plans that involve vm_restore()
|
||||
#
|
||||
|
||||
|
||||
class DecortController(object):
|
||||
"""DecortController is a utility class that holds target controller context and handles API requests formatting
|
||||
based on the requested authentication type.
|
||||
@@ -761,9 +747,12 @@ class DecortController(object):
|
||||
def kvmvm_provision(self, rg_id,
|
||||
comp_name, arch,
|
||||
cpu, ram,
|
||||
boot_disk, image_id,
|
||||
boot_disk,
|
||||
image_id,
|
||||
annotation="",
|
||||
userdata=None,
|
||||
sep_id=None,
|
||||
pool_name=None,
|
||||
start_on_create=True):
|
||||
"""Manage KVM VM provisioning. To remove existing KVM VM compute instance use compute_remove method,
|
||||
to resize use compute_resize, to manage power state use compute_powerstate method.
|
||||
@@ -808,6 +797,8 @@ class DecortController(object):
|
||||
cpu=cpu, ram=ram,
|
||||
imageId=image_id,
|
||||
bootDisk=boot_disk,
|
||||
sepId=sep_id,
|
||||
pool=pool_name,
|
||||
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:
|
||||
@@ -1189,46 +1180,118 @@ class DecortController(object):
|
||||
return False
|
||||
|
||||
def compute_affinity(self,comp_dict,tags,aff,aaff,label=""):
|
||||
|
||||
"""
|
||||
Manage Compute Tags,Affinitylabel and rules
|
||||
@param (dict) comp_dict: dictionary of the Compute parameters
|
||||
@param (dict) tags: dictionary of the tags
|
||||
@param (list) aff: affinity rules
|
||||
@param (list) aaff: antiaffinity rules
|
||||
@param (str) label: affinity group 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:
|
||||
|
||||
for tag in tags.items():
|
||||
if tag not in comp_dict['tags'].items():
|
||||
api_params = dict(computeId=comp_dict['id'],
|
||||
key=tag['key'],
|
||||
value=tag['value'], )
|
||||
key=tag[0],
|
||||
value=tag[1], )
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/tagAdd", api_params)
|
||||
if label:
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
|
||||
for tag in comp_dict['tags'].items():
|
||||
if tag not in tags.items():
|
||||
api_params = dict(computeId=comp_dict['id'],
|
||||
key=tag[0],)
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/tagRemove", api_params)
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
|
||||
if label and comp_dict['affinityLabel'] != label:
|
||||
api_params = dict(computeId=comp_dict['id'],
|
||||
affinityLabel=label,)
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/affinityLabelSet", api_params)
|
||||
if aff:
|
||||
if len(aff)>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 aaff:
|
||||
if len(aaff)>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
|
||||
elif label == "" and comp_dict['affinityLabel']:
|
||||
api_params = dict(computeId=comp_dict['id'])
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/compute/affinityLabelRemove", api_params)
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
affrule_del = []
|
||||
affrule_add = []
|
||||
aaffrule_del = []
|
||||
aaffrule_add = []
|
||||
|
||||
#AFFINITY
|
||||
for rule in comp_dict['affinityRules']:
|
||||
del rule['guid']
|
||||
if rule not in aff:
|
||||
affrule_del.append(rule)
|
||||
|
||||
for rule in aff:
|
||||
if rule not in comp_dict['affinityRules']:
|
||||
affrule_add.append(rule)
|
||||
|
||||
#ANTI AFFINITY
|
||||
for rule in comp_dict['antiAffinityRules']:
|
||||
del rule['guid']
|
||||
if rule not in aaff:
|
||||
aaffrule_del.append(rule)
|
||||
|
||||
for rule in aaff:
|
||||
if rule not in comp_dict['antiAffinityRules']:
|
||||
aaffrule_add.append(rule)
|
||||
|
||||
#AFFINITY
|
||||
if len (affrule_del):
|
||||
for rule in affrule_del:
|
||||
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/affinityRuleRemove", api_params)
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
if len(affrule_add)>0:
|
||||
for rule in affrule_add:
|
||||
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)
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
#ANTI AFFINITY
|
||||
if len(aaffrule_del):
|
||||
for rule in aaffrule_del:
|
||||
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/antiAffinityRuleRemove", api_params)
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
|
||||
if len(aaffrule_add)>0:
|
||||
for rule in aaffrule_add:
|
||||
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
|
||||
return
|
||||
###################################
|
||||
# OS image manipulation methods
|
||||
###################################
|
||||
@@ -1571,15 +1634,18 @@ class DecortController(object):
|
||||
self.result['msg'] = "rg_find(): cannot find RG by name if account ID is zero or less."
|
||||
self.amodule.fail_json(**self.result)
|
||||
# try to locate RG by name - start with getting all RGs IDs within the specified account
|
||||
api_params['accountId'] = arg_account_id
|
||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/account/listRG", api_params)
|
||||
#api_params['accountId'] = arg_account_id
|
||||
api_params['includedeleted'] = False
|
||||
#api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/account/listRG", api_params)
|
||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/rg/list",api_params)
|
||||
if api_resp.status_code == 200:
|
||||
account_specs = json.loads(api_resp.content.decode('utf8'))
|
||||
api_params.pop('accountId')
|
||||
#api_params.pop('accountId')
|
||||
for rg_item in account_specs:
|
||||
got_id, got_specs = self._rg_get_by_id(rg_item['id'])
|
||||
if got_id and got_specs['name'] == arg_rg_name:
|
||||
#
|
||||
if rg_item['name'] == arg_rg_name:
|
||||
# name matches
|
||||
got_id, got_specs = self._rg_get_by_id(rg_item['id'])
|
||||
if not arg_check_state or got_specs['status'] not in RG_INVALID_STATES:
|
||||
ret_rg_id = got_id
|
||||
ret_rg_dict = got_specs
|
||||
@@ -2595,7 +2661,54 @@ class DecortController(object):
|
||||
# Disk management
|
||||
#
|
||||
##############################
|
||||
def disk_check_iotune_arg(self,iotune_list):
|
||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_check_iotune_arg")
|
||||
MIN_IOPS = 80
|
||||
total_bytes_sec=iotune_list['total_bytes_sec']
|
||||
read_bytes_sec=iotune_list['read_bytes_sec']
|
||||
write_bytes_sec=iotune_list['write_bytes_sec']
|
||||
total_iops_sec=iotune_list['total_iops_sec']
|
||||
read_iops_sec=iotune_list['read_iops_sec']
|
||||
write_iops_sec=iotune_list['write_iops_sec']
|
||||
total_bytes_sec_max=iotune_list['total_bytes_sec_max']
|
||||
read_bytes_sec_max=iotune_list['read_bytes_sec_max']
|
||||
write_bytes_sec_max=iotune_list['write_bytes_sec_max']
|
||||
total_iops_sec_max=iotune_list['total_iops_sec_max']
|
||||
read_iops_sec_max=iotune_list['read_iops_sec_max']
|
||||
write_iops_sec_max=iotune_list['write_iops_sec_max']
|
||||
size_iops_sec=iotune_list['size_iops_sec']
|
||||
|
||||
if total_iops_sec and (read_iops_sec or write_iops_sec):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = (f"total and read/write of iops_sec cannot be set at the same time")
|
||||
if total_bytes_sec and (read_bytes_sec or write_bytes_sec):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] = (f"total and read/write of bytes_sec cannot be set at the same time")
|
||||
if total_bytes_sec_max and (read_bytes_sec_max or write_bytes_sec_max):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] =(f"total and read/write of bytes_sec_max cannot be set at the same time")
|
||||
if total_iops_sec_max and (read_iops_sec_max or write_iops_sec_max):
|
||||
self.result['failed'] = True
|
||||
self.result['changed'] = False
|
||||
self.result['msg'] =(f"total and read/write of iops_sec_max cannot be set at the same time")
|
||||
|
||||
for arg, val in iotune_list.items():
|
||||
if arg in (
|
||||
"total_iops_sec",
|
||||
"read_iops_sec",
|
||||
"write_iops_sec",
|
||||
"total_iops_sec_max",
|
||||
"read_iops_sec_max",
|
||||
"write_iops_sec_max",
|
||||
"size_iops_sec",
|
||||
):
|
||||
if val and val < self.MIN_IOPS:
|
||||
self.result['msg'] = (f"{arg} was set below the minimum iops {MIN_IOPS}: {val} provided")
|
||||
return
|
||||
|
||||
def disk_delete(self, disk_id, permanently, detach, reason):
|
||||
"""Deletes specified Disk.
|
||||
|
||||
@@ -2654,7 +2767,7 @@ class DecortController(object):
|
||||
|
||||
return ret_disk_id, ret_disk_dict
|
||||
|
||||
def disk_find(self, disk_id, name, account_id, check_state=False):
|
||||
def disk_find(self, disk_id=0, name="", account_id=0, check_state=False):
|
||||
"""Find specified Disk.
|
||||
|
||||
@param (int) disk_id: ID of the Disk. If non-zero disk_id is specified, all other arguments
|
||||
@@ -2678,13 +2791,13 @@ class DecortController(object):
|
||||
if self.amodule.check_mode:
|
||||
self.result['failed'] = False
|
||||
self.result['msg'] = "disk_find() in check mode: find Disk ID {} / name '{}' was requested.".format(disk_id,
|
||||
disk_name)
|
||||
name)
|
||||
return
|
||||
|
||||
ret_disk_id = 0
|
||||
ret_disk_facts = None
|
||||
|
||||
if disk_id > 0:
|
||||
if disk_id:
|
||||
ret_disk_id, ret_disk_facts = self._disk_get_by_id(disk_id)
|
||||
if not ret_disk_id:
|
||||
self.result['failed'] = True
|
||||
@@ -2694,19 +2807,20 @@ class DecortController(object):
|
||||
return ret_disk_id, ret_disk_facts
|
||||
else:
|
||||
return 0, None
|
||||
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)
|
||||
elif name:
|
||||
if account_id:
|
||||
api_params = dict(accountId=account_id,name=name)
|
||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/search", api_params)
|
||||
# the above call may return more than one matching disk
|
||||
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'] == name:
|
||||
if not check_state or runner['status']:
|
||||
return runner['id'], runner
|
||||
else:
|
||||
if len(disks_list) == 0:
|
||||
return 0, None
|
||||
elif len(disks_list) > 1:
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = "disk_find(): Found more then one Disk with Name: {}.".format(name)
|
||||
self.amodule.fail_json(**self.result)
|
||||
else:
|
||||
return disks_list[0]['id'], disks_list[0]
|
||||
else: # we are missing meaningful account_id - fail the module
|
||||
self.result['failed'] = True
|
||||
self.result['msg'] = ("disk_find(): cannot find Disk by name '{}' "
|
||||
@@ -2719,7 +2833,7 @@ class DecortController(object):
|
||||
|
||||
return 0, None
|
||||
|
||||
def disk_create(self, accountId, gid, name, description, size, type, iops, sep_id, pool):
|
||||
def disk_create(self, accountId, 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
|
||||
@@ -2739,13 +2853,13 @@ class DecortController(object):
|
||||
|
||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_creation")
|
||||
api_params = dict(accountId=accountId,
|
||||
gid=gid,
|
||||
gid=0, # depricated
|
||||
name=name,
|
||||
description=description,
|
||||
size=size,
|
||||
type=type,
|
||||
iops=iops,
|
||||
sepId=sep_id,
|
||||
sep_id=sep_id,
|
||||
pool=pool )
|
||||
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/create", api_params)
|
||||
if api_resp.status_code == 200:
|
||||
@@ -2807,32 +2921,21 @@ class DecortController(object):
|
||||
|
||||
return
|
||||
|
||||
def disk_limitIO(self, limits, diskId):
|
||||
def disk_limitIO(self,disk_id, limits):
|
||||
"""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'])
|
||||
api_params = dict(diskId=disk_id,
|
||||
**limits)
|
||||
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)
|
||||
self.result['changed'] = True
|
||||
self.result['msg'] = "Specified Disk ID {} limited successfully.".format(disk_id)
|
||||
return
|
||||
|
||||
def disk_rename(self, diskId, name):
|
||||
def disk_rename(self, disk_id, name):
|
||||
"""Renames disk to the specified new name.
|
||||
|
||||
@param disk_id: ID of the Disk to rename.
|
||||
@@ -2841,12 +2944,13 @@ class DecortController(object):
|
||||
@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,
|
||||
api_params = dict(diskId=disk_id,
|
||||
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
|
||||
self.result['msg'] = ("Disk with id '{}',successfully renamed to '{}'.").format(disk_id, name)
|
||||
return
|
||||
|
||||
def disk_restore(self, disk_id):
|
||||
@@ -2872,6 +2976,30 @@ class DecortController(object):
|
||||
self.result['failed'] = False
|
||||
self.result['changed'] = True
|
||||
return
|
||||
def disk_share(self, disk_id, share='false'):
|
||||
"""Share data disk
|
||||
|
||||
@param disk_id: ID of the Disk to share.
|
||||
|
||||
@param share: share status of the disk
|
||||
|
||||
@returns: nothing on success. On error this method will abort module execution.
|
||||
"""
|
||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "disk_share")
|
||||
if self.amodule.check_mode:
|
||||
self.result['failed'] = False
|
||||
self.result['msg'] = "disk_share() in check mode: share Disk ID {} was requested.".format(disk_id)
|
||||
return
|
||||
|
||||
api_params = dict(diskId=disk_id)
|
||||
if share:
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/share", api_params)
|
||||
else:
|
||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/disks/unshare", 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
|
||||
|
||||
##############################
|
||||
#
|
||||
@@ -3140,6 +3268,10 @@ class DecortController(object):
|
||||
for k8s_item in k8s_list:
|
||||
if k8s_item['name'] == k8s_name and k8s_item['rgId'] == rg_id:
|
||||
if not check_state or k8s_item['status'] not in K8S_INVALID_STATES:
|
||||
# TODO: rework after k8s/get wilb be updated
|
||||
self.k8s_vins_id = None
|
||||
self.k8s_vins_id = k8s_item['vinsId']
|
||||
#
|
||||
ret_k8s_id = k8s_item['id']
|
||||
_, ret_k8s_dict = self._k8s_get_by_id(ret_k8s_id)
|
||||
|
||||
@@ -3264,7 +3396,7 @@ class DecortController(object):
|
||||
return
|
||||
|
||||
def k8s_provision(self, k8s_name,
|
||||
k8ci_id,rg_id, master_count,
|
||||
k8ci_id,rg_id,plugin,master_count,
|
||||
master_cpu, master_ram,
|
||||
master_disk, default_worker, extnet_id,
|
||||
with_lb, annotation, ):
|
||||
@@ -3290,6 +3422,7 @@ class DecortController(object):
|
||||
rgId=rg_id,
|
||||
k8ciId=k8ci_id,
|
||||
workerGroupName=def_wg_name,
|
||||
networkPlugin=plugin,
|
||||
masterNum=master_count,
|
||||
masterCpu=master_cpu,
|
||||
masterRam=master_ram,
|
||||
@@ -3326,6 +3459,7 @@ class DecortController(object):
|
||||
return
|
||||
elif ret_info['status'] == "OK":
|
||||
k8s_id = ret_info['result']
|
||||
self.result['msg'] = f"k8s_provision(): K8s cluster {k8s_name} created successful"
|
||||
self.result['changed'] = True
|
||||
return k8s_id
|
||||
else:
|
||||
@@ -3339,6 +3473,9 @@ class DecortController(object):
|
||||
else:
|
||||
self.result['msg'] = ("k8s_provision(): Can't create cluster")
|
||||
self.result['failed'] = True
|
||||
|
||||
self.result['changed'] = False
|
||||
self.fail_json(**self.result)
|
||||
return
|
||||
|
||||
def k8s_workers_modify(self,arg_k8swg,arg_modwg):
|
||||
|
||||
Reference in New Issue
Block a user