Skip to content

Commit 19b7cf2

Browse files
lyarwoodmelwitt
authored andcommitted
manage: Add image_property commands
This adds an image property show and image property set command to nova-manage to allow users to update image properties stored for an instance in system metadata without having to rebuild the instance. This is intended to ease migration to new machine types, as updating the machine type could potentially invalidate the existing image properties of an instance. Co-Authored-By: melanie witt <[email protected]> Blueprint: libvirt-device-bus-model-update Change-Id: Ic8783053778cf4614742186e94059d5675121db1
1 parent 7ecdfb6 commit 19b7cf2

File tree

7 files changed

+732
-1
lines changed

7 files changed

+732
-1
lines changed

doc/source/admin/hw-machine-type.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ hw_machine_type - Configuring and updating QEMU instance machine types
2525

2626
Added ``nova-manage`` commands to control the machine_type of an instance.
2727

28+
.. versionchanged:: 25.0.0 (Yoga)
29+
30+
Added ``nova-manage`` commands to set the image properties of an instance.
31+
2832
.. note::
2933

3034
The following only applies to environments using libvirt compute hosts.
@@ -135,3 +139,30 @@ Once it has been verified that all instances within the environment or specific
135139
cell have had a machine type recorded then the
136140
:oslo.config:option:`libvirt.hw_machine_type` can be updated without impacting
137141
existing instances.
142+
143+
Device bus and model image properties
144+
-------------------------------------
145+
146+
.. versionadded:: 25.0.0 (Yoga)
147+
148+
Device bus and model types defined as image properties associated with an
149+
instance are always used when launching instances with the libvirt driver.
150+
Support for each device bus and model is dependent on the machine type used and
151+
version of QEMU available on the underlying compute host. As such, any changes
152+
to the machine type of an instance or version of QEMU on a host might suddenly
153+
invalidate the stored device bus or model image properties.
154+
155+
It is now possible to change the stored image properties of an instance without
156+
having to rebuild the instance.
157+
158+
To show the stored image properties of an instance:
159+
160+
.. code-block:: shell
161+
162+
$ nova-manage image_property show $instance_uuid $property
163+
164+
To update the stored image properties of an instance:
165+
166+
.. code-block:: shell
167+
168+
$ nova-manage image_property set $instance_uuid --property $property

doc/source/cli/nova-manage.rst

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,91 @@ within an environment.
16681668
* - 3
16691669
- Instances found without ``hw_machine_type`` set
16701670

1671+
Image Property Commands
1672+
=======================
1673+
1674+
image_property show
1675+
-------------------
1676+
1677+
.. program:: nova-manage image_property show
1678+
1679+
.. code-block:: shell
1680+
1681+
nova-manage image_property show [INSTANCE_UUID] [IMAGE_PROPERTY]
1682+
1683+
Fetch and display the recorded image property ``IMAGE_PROPERTY`` of an
1684+
instance identified by ``INSTANCE_UUID``.
1685+
1686+
.. versionadded:: 25.0.0 (Yoga)
1687+
1688+
.. rubric:: Return codes
1689+
1690+
.. list-table::
1691+
:widths: 20 80
1692+
:header-rows: 1
1693+
1694+
* - Return code
1695+
- Description
1696+
* - 0
1697+
- Successfully completed
1698+
* - 1
1699+
- An unexpected error occurred
1700+
* - 2
1701+
- Unable to find instance or instance mapping
1702+
* - 3
1703+
- No image property found for instance
1704+
1705+
image_property set
1706+
------------------
1707+
1708+
.. program:: nova-manage image_property set
1709+
1710+
.. code-block:: shell
1711+
1712+
nova-manage image_property set \
1713+
[INSTANCE_UUID] [--property] [IMAGE_PROPERTY]=[VALUE]
1714+
1715+
Set or update the recorded image property ``IMAGE_PROPERTY`` of instance
1716+
``INSTANCE_UUID`` to value ``VALUE``.
1717+
1718+
The following criteria must be met when using this command:
1719+
1720+
* The instance must have a ``vm_state`` of ``STOPPED``, ``SHELVED`` or
1721+
``SHELVED_OFFLOADED``.
1722+
1723+
This command is useful for operators who need to update stored instance image
1724+
properties that have become invalidated by a change of instance machine type,
1725+
for example.
1726+
1727+
.. versionadded:: 25.0.0 (Yoga)
1728+
1729+
.. rubric:: Options
1730+
1731+
.. option:: --property
1732+
1733+
Image property to set using the format name=value. For example:
1734+
``--property hw_disk_bus=virtio --property hw_cdrom_bus=sata``.
1735+
1736+
.. rubric:: Return codes
1737+
1738+
.. list-table::
1739+
:widths: 20 80
1740+
:header-rows: 1
1741+
1742+
* - Return code
1743+
- Description
1744+
* - 0
1745+
- Update completed successfully
1746+
* - 1
1747+
- An unexpected error occurred
1748+
* - 2
1749+
- Unable to find instance or instance mapping
1750+
* - 3
1751+
- The instance has an invalid ``vm_state``
1752+
* - 4
1753+
- The provided image property name is invalid
1754+
* - 5
1755+
- The provided image property value is invalid
16711756

16721757
See Also
16731758
========

nova/cmd/manage.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,13 +3192,176 @@ def refresh(self, instance_uuid=None, volume_id=None, connector_path=None):
31923192
return 1
31933193

31943194

3195+
class ImagePropertyCommands():
3196+
3197+
@action_description(_("Show the value of an instance image property."))
3198+
@args(
3199+
'instance_uuid', metavar='<instance_uuid>',
3200+
help='UUID of the instance')
3201+
@args(
3202+
'property', metavar='<image_property>',
3203+
help='Image property to show')
3204+
def show(self, instance_uuid=None, image_property=None):
3205+
"""Show value of a given instance image property.
3206+
3207+
Return codes:
3208+
* 0: Command completed successfully.
3209+
* 1: An unexpected error happened.
3210+
* 2: Instance not found.
3211+
* 3: Image property not found.
3212+
"""
3213+
try:
3214+
ctxt = context.get_admin_context()
3215+
im = objects.InstanceMapping.get_by_instance_uuid(
3216+
ctxt, instance_uuid)
3217+
with context.target_cell(ctxt, im.cell_mapping) as cctxt:
3218+
instance = objects.Instance.get_by_uuid(
3219+
cctxt, instance_uuid, expected_attrs=['system_metadata'])
3220+
image_property = instance.system_metadata.get(
3221+
f'image_{image_property}')
3222+
if image_property:
3223+
print(image_property)
3224+
return 0
3225+
else:
3226+
print(f'Image property {image_property} not found '
3227+
f'for instance {instance_uuid}.')
3228+
return 3
3229+
except (
3230+
exception.InstanceNotFound,
3231+
exception.InstanceMappingNotFound,
3232+
) as e:
3233+
print(str(e))
3234+
return 2
3235+
except Exception as e:
3236+
print(f'Unexpected error, see nova-manage.log for the full '
3237+
f'trace: {str(e)}')
3238+
LOG.exception('Unexpected error')
3239+
return 1
3240+
3241+
def _validate_image_properties(self, image_properties):
3242+
"""Validate the provided image property names and values
3243+
3244+
:param image_properties: List of image property names and values
3245+
"""
3246+
# Sanity check the format of the provided properties, this should be
3247+
# in the format of name=value.
3248+
if any(x for x in image_properties if '=' not in x):
3249+
raise exception.InvalidInput(
3250+
"--property should use the format key=value")
3251+
3252+
# Transform the list of delimited properties to a dict
3253+
image_properties = dict(prop.split('=') for prop in image_properties)
3254+
3255+
# Validate the names of each property by checking against the o.vo
3256+
# fields currently listed by ImageProps. We can't use from_dict to
3257+
# do this as it silently ignores invalid property keys.
3258+
for image_property_name in image_properties.keys():
3259+
if image_property_name not in objects.ImageMetaProps.fields:
3260+
raise exception.InvalidImagePropertyName(
3261+
image_property_name=image_property_name)
3262+
3263+
# Validate the values by creating an object from the provided dict.
3264+
objects.ImageMetaProps.from_dict(image_properties)
3265+
3266+
# Return the dict so we can update the instance system_metadata
3267+
return image_properties
3268+
3269+
def _update_image_properties(self, instance, image_properties):
3270+
"""Update instance image properties
3271+
3272+
:param instance: The instance to update
3273+
:param image_properties: List of image properties and values to update
3274+
"""
3275+
# Check the state of the instance
3276+
allowed_states = [
3277+
obj_fields.InstanceState.STOPPED,
3278+
obj_fields.InstanceState.SHELVED,
3279+
obj_fields.InstanceState.SHELVED_OFFLOADED,
3280+
]
3281+
if instance.vm_state not in allowed_states:
3282+
raise exception.InstanceInvalidState(
3283+
instance_uuid=instance.uuid, attr='vm_state',
3284+
state=instance.vm_state,
3285+
method='image_property set (must be STOPPED, SHELVED, OR '
3286+
'SHELVED_OFFLOADED).')
3287+
3288+
# Validate the property names and values
3289+
image_properties = self._validate_image_properties(image_properties)
3290+
3291+
# Update the image properties and save the instance record
3292+
for image_property, value in image_properties.items():
3293+
instance.system_metadata[f'image_{image_property}'] = value
3294+
3295+
# Save and return 0
3296+
instance.save()
3297+
return 0
3298+
3299+
@action_description(_(
3300+
"Set the values of instance image properties stored in the database. "
3301+
"This is only allowed for " "instances with a STOPPED, SHELVED or "
3302+
"SHELVED_OFFLOADED vm_state."))
3303+
@args(
3304+
'instance_uuid', metavar='<instance_uuid>',
3305+
help='UUID of the instance')
3306+
@args(
3307+
'--property', metavar='<image_property>', action='append',
3308+
dest='image_properties',
3309+
help='Image property to set using the format name=value. For example: '
3310+
'--property hw_disk_bus=virtio --property hw_cdrom_bus=sata')
3311+
def set(self, instance_uuid=None, image_properties=None):
3312+
"""Set instance image property values
3313+
3314+
Return codes:
3315+
* 0: Command completed successfully.
3316+
* 1: An unexpected error happened.
3317+
* 2: Unable to find instance.
3318+
* 3: Instance is in an invalid state.
3319+
* 4: Invalid input format.
3320+
* 5: Invalid image property name.
3321+
* 6: Invalid image property value.
3322+
"""
3323+
try:
3324+
ctxt = context.get_admin_context()
3325+
im = objects.InstanceMapping.get_by_instance_uuid(
3326+
ctxt, instance_uuid)
3327+
with context.target_cell(ctxt, im.cell_mapping) as cctxt:
3328+
instance = objects.Instance.get_by_uuid(
3329+
cctxt, instance_uuid, expected_attrs=['system_metadata'])
3330+
return self._update_image_properties(
3331+
instance, image_properties)
3332+
except ValueError as e:
3333+
print(str(e))
3334+
return 6
3335+
except exception.InvalidImagePropertyName as e:
3336+
print(str(e))
3337+
return 5
3338+
except exception.InvalidInput as e:
3339+
print(str(e))
3340+
return 4
3341+
except exception.InstanceInvalidState as e:
3342+
print(str(e))
3343+
return 3
3344+
except (
3345+
exception.InstanceNotFound,
3346+
exception.InstanceMappingNotFound,
3347+
) as e:
3348+
print(str(e))
3349+
return 2
3350+
except Exception as e:
3351+
print('Unexpected error, see nova-manage.log for the full '
3352+
'trace: %s ' % str(e))
3353+
LOG.exception('Unexpected error')
3354+
return 1
3355+
3356+
31953357
CATEGORIES = {
31963358
'api_db': ApiDbCommands,
31973359
'cell_v2': CellV2Commands,
31983360
'db': DbCommands,
31993361
'placement': PlacementCommands,
32003362
'libvirt': LibvirtCommands,
32013363
'volume_attachment': VolumeAttachmentCommands,
3364+
'image_property': ImagePropertyCommands,
32023365
}
32033366

32043367

nova/exception.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,10 @@ class InvalidImageRef(Invalid):
736736
msg_fmt = _("Invalid image href %(image_href)s.")
737737

738738

739+
class InvalidImagePropertyName(Invalid):
740+
msg_fmt = _("Invalid image property name %(image_property_name)s.")
741+
742+
739743
class AutoDiskConfigDisabledByImage(Invalid):
740744
msg_fmt = _("Requested image %(image)s "
741745
"has automatic disk resize disabled.")

0 commit comments

Comments
 (0)