Refactored compute_flavor module
Change-Id: Id023d13abf5c1179044fcc611a3e509f16f10621
This commit is contained in:
parent
114bc855c0
commit
d3c5ddd40f
@ -13,52 +13,19 @@ description:
|
|||||||
- Add or remove compute flavors from OpenStack.
|
- Add or remove compute flavors from OpenStack.
|
||||||
- Updating a flavor consists of deleting and (re)creating a flavor.
|
- Updating a flavor consists of deleting and (re)creating a flavor.
|
||||||
options:
|
options:
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Indicate desired state of the resource. When I(state) is 'present',
|
|
||||||
then I(ram), I(vcpus), and I(disk) are all required. There are no
|
|
||||||
default values for those parameters.
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
default: present
|
|
||||||
type: str
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Flavor name.
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
ram:
|
|
||||||
description:
|
|
||||||
- Amount of memory, in MB.
|
|
||||||
type: int
|
|
||||||
vcpus:
|
|
||||||
description:
|
|
||||||
- Number of virtual CPUs.
|
|
||||||
type: int
|
|
||||||
disk:
|
disk:
|
||||||
description:
|
description:
|
||||||
- Size of local disk, in GB.
|
- Size of local disk, in GB.
|
||||||
default: 0
|
- Required when I(state) is C(present).
|
||||||
type: int
|
type: int
|
||||||
ephemeral:
|
ephemeral:
|
||||||
description:
|
description:
|
||||||
- Ephemeral space size, in GB.
|
- Ephemeral space size, in GB.
|
||||||
default: 0
|
|
||||||
type: int
|
type: int
|
||||||
swap:
|
extra_specs:
|
||||||
description:
|
description:
|
||||||
- Swap space size, in MB.
|
- Metadata dictionary
|
||||||
default: 0
|
type: dict
|
||||||
type: int
|
|
||||||
rxtx_factor:
|
|
||||||
description:
|
|
||||||
- RX/TX factor.
|
|
||||||
default: 1.0
|
|
||||||
type: float
|
|
||||||
is_public:
|
|
||||||
description:
|
|
||||||
- Make flavor accessible to the public.
|
|
||||||
type: bool
|
|
||||||
default: 'yes'
|
|
||||||
id:
|
id:
|
||||||
description:
|
description:
|
||||||
- ID for the flavor. This is optional as a unique UUID will be
|
- ID for the flavor. This is optional as a unique UUID will be
|
||||||
@ -70,10 +37,41 @@ options:
|
|||||||
will be dropped in the next major release.
|
will be dropped in the next major release.
|
||||||
type: str
|
type: str
|
||||||
aliases: ['flavorid']
|
aliases: ['flavorid']
|
||||||
extra_specs:
|
is_public:
|
||||||
description:
|
description:
|
||||||
- Metadata dictionary
|
- Make flavor accessible to the public.
|
||||||
type: dict
|
type: bool
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- Flavor name.
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
ram:
|
||||||
|
description:
|
||||||
|
- Amount of memory, in MB.
|
||||||
|
- Required when I(state) is C(present).
|
||||||
|
type: int
|
||||||
|
rxtx_factor:
|
||||||
|
description:
|
||||||
|
- RX/TX factor.
|
||||||
|
type: float
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Indicate desired state of the resource.
|
||||||
|
- When I(state) is C(present), then I(ram), I(vcpus), and I(disk) are
|
||||||
|
required. There are no default values for those parameters.
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
default: present
|
||||||
|
type: str
|
||||||
|
swap:
|
||||||
|
description:
|
||||||
|
- Swap space size, in MB.
|
||||||
|
type: int
|
||||||
|
vcpus:
|
||||||
|
description:
|
||||||
|
- Number of virtual CPUs.
|
||||||
|
- Required when I(state) is C(present).
|
||||||
|
type: int
|
||||||
requirements:
|
requirements:
|
||||||
- "python >= 3.6"
|
- "python >= 3.6"
|
||||||
- "openstacksdk"
|
- "openstacksdk"
|
||||||
@ -192,21 +190,17 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
|||||||
|
|
||||||
class ComputeFlavorModule(OpenStackModule):
|
class ComputeFlavorModule(OpenStackModule):
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
state=dict(default='present',
|
disk=dict(type='int'),
|
||||||
choices=['absent', 'present']),
|
ephemeral=dict(type='int'),
|
||||||
name=dict(required=True),
|
|
||||||
|
|
||||||
# required when state is 'present'
|
|
||||||
ram=dict(type='int'),
|
|
||||||
vcpus=dict(type='int'),
|
|
||||||
|
|
||||||
disk=dict(default=0, type='int'),
|
|
||||||
ephemeral=dict(default=0, type='int'),
|
|
||||||
swap=dict(default=0, type='int'),
|
|
||||||
rxtx_factor=dict(default=1.0, type='float'),
|
|
||||||
is_public=dict(default=True, type='bool'),
|
|
||||||
id=dict(aliases=['flavorid']),
|
|
||||||
extra_specs=dict(type='dict'),
|
extra_specs=dict(type='dict'),
|
||||||
|
id=dict(aliases=['flavorid']),
|
||||||
|
is_public=dict(type='bool'),
|
||||||
|
name=dict(required=True),
|
||||||
|
ram=dict(type='int'),
|
||||||
|
rxtx_factor=dict(type='float'),
|
||||||
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
|
swap=dict(type='int'),
|
||||||
|
vcpus=dict(type='int'),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = dict(
|
module_kwargs = dict(
|
||||||
@ -216,93 +210,149 @@ class ComputeFlavorModule(OpenStackModule):
|
|||||||
supports_check_mode=True
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def _system_state_change(self, flavor, extra_specs, old_extra_specs):
|
|
||||||
state = self.params['state']
|
|
||||||
if state == 'present':
|
|
||||||
if not flavor:
|
|
||||||
return True
|
|
||||||
return self._needs_update(flavor) or extra_specs != old_extra_specs
|
|
||||||
if state == 'absent' and flavor:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _needs_update(self, flavor):
|
|
||||||
fields = ['ram', 'vcpus', 'disk', 'ephemeral', 'swap', 'rxtx_factor',
|
|
||||||
'is_public']
|
|
||||||
for k in fields:
|
|
||||||
if self.params[k] is not None and self.params[k] != flavor[k]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _build_flavor_specs_diff(self, extra_specs, old_extra_specs):
|
|
||||||
new_extra_specs = dict([(k, str(v)) for k, v in extra_specs.items()])
|
|
||||||
unset_keys = set(old_extra_specs.keys()) - set(extra_specs.keys())
|
|
||||||
return new_extra_specs, unset_keys
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
state = self.params['state']
|
state = self.params['state']
|
||||||
|
id = self.params['id']
|
||||||
name = self.params['name']
|
name = self.params['name']
|
||||||
extra_specs = self.params['extra_specs'] or {}
|
name_or_id = id if id and id != 'auto' else name
|
||||||
|
flavor = self.conn.compute.find_flavor(name_or_id,
|
||||||
flavor = self.conn.compute.find_flavor(name, get_extra_specs=True)
|
get_extra_specs=True)
|
||||||
old_extra_specs = {}
|
|
||||||
if flavor:
|
|
||||||
old_extra_specs = flavor['extra_specs']
|
|
||||||
if flavor['swap'] == '':
|
|
||||||
flavor['swap'] = 0
|
|
||||||
|
|
||||||
if self.ansible.check_mode:
|
if self.ansible.check_mode:
|
||||||
self.exit_json(changed=self._system_state_change(
|
self.exit_json(changed=self._will_change(state, flavor))
|
||||||
flavor, extra_specs, old_extra_specs))
|
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present' and not flavor:
|
||||||
flavor_id = self.params['id']
|
# Create flavor
|
||||||
# Keep for backward compatibility
|
flavor = self._create()
|
||||||
flavor_id = None if flavor_id == 'auto' else flavor_id
|
self.exit_json(changed=True,
|
||||||
if flavor and self._needs_update(flavor):
|
flavor=flavor.to_dict(computed=False))
|
||||||
# Because only flavor descriptions are updateable, we have to
|
|
||||||
# delete and recreate a flavor to "update" it
|
|
||||||
flavor_id = flavor['id']
|
|
||||||
self.conn.compute.delete_flavor(flavor)
|
|
||||||
old_extra_specs = {}
|
|
||||||
flavor = None
|
|
||||||
|
|
||||||
changed = False
|
elif state == 'present' and flavor:
|
||||||
if not flavor:
|
# Update flavor
|
||||||
flavor = self.conn.compute.create_flavor(
|
update = self._build_update(flavor)
|
||||||
name=name,
|
if update:
|
||||||
ram=self.params['ram'],
|
flavor = self._update(flavor, update)
|
||||||
vcpus=self.params['vcpus'],
|
|
||||||
disk=self.params['disk'],
|
|
||||||
id=flavor_id,
|
|
||||||
ephemeral=self.params['ephemeral'],
|
|
||||||
swap=self.params['swap'],
|
|
||||||
rxtx_factor=self.params['rxtx_factor'],
|
|
||||||
is_public=self.params['is_public']
|
|
||||||
)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
new_extra_specs, unset_keys = self._build_flavor_specs_diff(
|
self.exit_json(changed=bool(update),
|
||||||
extra_specs, old_extra_specs)
|
flavor=flavor.to_dict(computed=False))
|
||||||
|
|
||||||
if unset_keys:
|
elif state == 'absent' and flavor:
|
||||||
self.conn.unset_flavor_specs(flavor['id'], unset_keys)
|
# Delete flavor
|
||||||
|
self._delete(flavor)
|
||||||
|
self.exit_json(changed=True)
|
||||||
|
|
||||||
if old_extra_specs != new_extra_specs:
|
elif state == 'absent' and not flavor:
|
||||||
self.conn.compute.create_flavor_extra_specs(
|
# Do nothing
|
||||||
flavor['id'], extra_specs)
|
self.exit_json(changed=False)
|
||||||
changed = True
|
|
||||||
|
|
||||||
# Have to refetch updated extra_specs
|
def _build_update(self, flavor):
|
||||||
|
return {
|
||||||
|
**self._build_update_extra_specs(flavor),
|
||||||
|
**self._build_update_flavor(flavor)}
|
||||||
|
|
||||||
|
def _build_update_extra_specs(self, flavor):
|
||||||
|
update = {}
|
||||||
|
|
||||||
|
old_extra_specs = flavor['extra_specs']
|
||||||
|
new_extra_specs = self.params['extra_specs'] or {}
|
||||||
|
if flavor['swap'] == '':
|
||||||
|
flavor['swap'] = 0
|
||||||
|
|
||||||
|
delete_extra_specs_keys = \
|
||||||
|
set(old_extra_specs.keys()) - set(new_extra_specs.keys())
|
||||||
|
|
||||||
|
if delete_extra_specs_keys:
|
||||||
|
update['delete_extra_specs_keys'] = delete_extra_specs_keys
|
||||||
|
|
||||||
|
stringified = dict([(k, str(v))
|
||||||
|
for k, v in new_extra_specs.items()])
|
||||||
|
|
||||||
|
if old_extra_specs != stringified:
|
||||||
|
update['create_extra_specs'] = new_extra_specs
|
||||||
|
|
||||||
|
return update
|
||||||
|
|
||||||
|
def _build_update_flavor(self, flavor):
|
||||||
|
update = {}
|
||||||
|
|
||||||
|
flavor_attributes = dict(
|
||||||
|
(k, self.params[k])
|
||||||
|
for k in ['ram', 'vcpus', 'disk', 'ephemeral', 'swap',
|
||||||
|
'rxtx_factor', 'is_public']
|
||||||
|
if k in self.params and self.params[k] is not None
|
||||||
|
and self.params[k] != flavor[k])
|
||||||
|
|
||||||
|
if flavor_attributes:
|
||||||
|
update['flavor_attributes'] = flavor_attributes
|
||||||
|
|
||||||
|
return update
|
||||||
|
|
||||||
|
def _create(self):
|
||||||
|
kwargs = dict((k, self.params[k])
|
||||||
|
for k in ['name', 'ram', 'vcpus', 'disk', 'ephemeral',
|
||||||
|
'swap', 'rxtx_factor', 'is_public']
|
||||||
|
if self.params[k] is not None)
|
||||||
|
|
||||||
|
# Keep for backward compatibility
|
||||||
|
id = self.params['id']
|
||||||
|
if id is not None and id != 'auto':
|
||||||
|
kwargs['id'] = id
|
||||||
|
|
||||||
|
flavor = self.conn.compute.create_flavor(**kwargs)
|
||||||
|
|
||||||
|
extra_specs = self.params['extra_specs']
|
||||||
|
if extra_specs:
|
||||||
|
flavor = self.conn.compute.create_flavor_extra_specs(flavor.id,
|
||||||
|
extra_specs)
|
||||||
|
|
||||||
|
return flavor
|
||||||
|
|
||||||
|
def _delete(self, flavor):
|
||||||
|
self.conn.compute.delete_flavor(flavor)
|
||||||
|
|
||||||
|
def _update(self, flavor, update):
|
||||||
|
flavor = self._update_flavor(flavor, update)
|
||||||
|
flavor = self._update_extra_specs(flavor, update)
|
||||||
|
return flavor
|
||||||
|
|
||||||
|
def _update_extra_specs(self, flavor, update):
|
||||||
|
if update.get('flavor_attributes'):
|
||||||
|
# No need to update extra_specs since flavor will be recreated
|
||||||
|
return flavor
|
||||||
|
|
||||||
|
delete_extra_specs_keys = update.get('delete_extra_specs_keys')
|
||||||
|
if delete_extra_specs_keys:
|
||||||
|
self.conn.unset_flavor_specs(flavor.id, delete_extra_specs_keys)
|
||||||
|
# Update flavor after extra_specs removal
|
||||||
flavor = self.conn.compute.fetch_flavor_extra_specs(flavor)
|
flavor = self.conn.compute.fetch_flavor_extra_specs(flavor)
|
||||||
|
|
||||||
self.exit_json(
|
create_extra_specs = update.get('create_extra_specs')
|
||||||
changed=changed, flavor=flavor.to_dict(computed=False))
|
if create_extra_specs:
|
||||||
|
flavor = self.conn.compute.create_flavor_extra_specs(
|
||||||
|
flavor.id, create_extra_specs)
|
||||||
|
|
||||||
elif state == 'absent':
|
return flavor
|
||||||
if flavor:
|
|
||||||
self.conn.compute.delete_flavor(flavor)
|
def _update_flavor(self, flavor, update):
|
||||||
self.exit_json(changed=True)
|
flavor_attributes = update.get('flavor_attributes')
|
||||||
self.exit_json(changed=False)
|
if flavor_attributes:
|
||||||
|
# Because only flavor descriptions are updateable,
|
||||||
|
# flavor has to be recreated to "update" it
|
||||||
|
self._delete(flavor)
|
||||||
|
flavor = self._create()
|
||||||
|
|
||||||
|
return flavor
|
||||||
|
|
||||||
|
def _will_change(self, state, flavor):
|
||||||
|
if state == 'present' and not flavor:
|
||||||
|
return True
|
||||||
|
elif state == 'present' and flavor:
|
||||||
|
return bool(self._build_update(flavor))
|
||||||
|
elif state == 'absent' and flavor:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# state == 'absent' and not flavor:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
Loading…
Reference in New Issue
Block a user