diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py index fa49c3db91..ac5b37dbe7 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/device_image.py @@ -162,11 +162,16 @@ def _get_applied_labels(device_image): applied_labels.append({label.label_key: label.label_value}) device_image.applied_labels = applied_labels - # if applied without labels, insert a null dict to applied-labels + # In the database, 'applied' field is True if the image is applied without + # labels and False if the image is not applied. + # In the api response, 'applied' field is True if the image is applied with + # or without labels. if device_image.applied: + # image is applied without labels, insert a null dict to applied-labels applied_labels.append({}) device_image.applied_labels = applied_labels elif device_image.applied_labels is not None: + # image is applied with labels, set the applied attribute to True device_image.applied = True return device_image @@ -327,13 +332,12 @@ class DeviceImageController(rest.RestController): update_device_image_state(device_label.host_id, device_label.pcidevice_id, device_image.id, dconstants.DEVICE_IMAGE_UPDATE_PENDING) + update_host_device_image_update(device_label.host_uuid) # Create an entry of image to label mapping pecan.request.dbapi.device_image_label_create({ 'image_id': device_image.id, 'label_id': device_label.id, }) - pecan.request.rpcapi.apply_device_image( - pecan.request.context, device_label.host_uuid) elif action == dconstants.REMOVE_ACTION: try: img_lbl = pecan.request.dbapi.device_image_label_get_by_image_label( @@ -365,17 +369,30 @@ class DeviceImageController(rest.RestController): update_device_image_state(host.id, dev.pci_id, device_image.id, dconstants.DEVICE_IMAGE_UPDATE_PENDING) - pecan.request.rpcapi.apply_device_image( - pecan.request.context, host.uuid) + update_host_device_image_update(host.uuid) elif action == dconstants.REMOVE_ACTION: delete_device_image_state(dev.pci_id, device_image) - # set applied flag to indicate if the image is applied without label or not + if action == dconstants.APPLY_ACTION: + # set applied flag to True to indicate the image is applied + # without label pecan.request.dbapi.deviceimage_update(device_image.uuid, {'applied': True}) - else: + elif action == dconstants.REMOVE_ACTION: + # set applied flag to False to indicate the image is not applied pecan.request.dbapi.deviceimage_update(device_image.uuid, {'applied': False}) + + if action == dconstants.APPLY_ACTION: + # If any of the devices needs updating, call rpc to raise alarm + if pecan.request.dbapi.device_image_state_get_all( + image_id=device_image.id, + status=[dconstants.DEVICE_IMAGE_UPDATE_PENDING, + dconstants.DEVICE_IMAGE_UPDATE_FAILED, + dconstants.DEVICE_IMAGE_UPDATE_IN_PROGRESS]): + pecan.request.rpcapi.apply_device_image(pecan.request.context) + elif action == dconstants.REMOVE_ACTION: + pecan.request.rpcapi.remove_device_image(pecan.request.context) device_image = objects.device_image.get_by_uuid(pecan.request.context, uuid) return DeviceImage.convert_with_links(device_image) @@ -447,6 +464,13 @@ def _validate_syntax(device_image): return msg +def update_host_device_image_update(host_uuid): + host = objects.host.get_by_uuid(pecan.request.context, host_uuid) + if host.device_image_update != dconstants.DEVICE_IMAGE_UPDATE_IN_PROGRESS: + host.device_image_update = dconstants.DEVICE_IMAGE_UPDATE_PENDING + host.save() + + def update_device_image_state(host_id, pcidevice_id, image_id, status): try: dev_img_state = pecan.request.dbapi.device_image_state_get_by_image_device( diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 8133563022..e8536ba624 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -11816,14 +11816,8 @@ class ConductorManager(service.PeriodicService): self.fm_api.clear_fault(fm_constants.FM_ALARM_ID_DEVICE_IMAGE_UPDATE_IN_PROGRESS, entity_instance_id) - def apply_device_image(self, context, host_uuid): + def apply_device_image(self, context): """Apply device image""" - if host_uuid is not None: - host = objects.host.get_by_uuid(context, host_uuid) - if host.device_image_update != dconstants.DEVICE_IMAGE_UPDATE_IN_PROGRESS: - host.device_image_update = dconstants.DEVICE_IMAGE_UPDATE_PENDING - host.save() - # Raise device image update alarm if not already exists alarm_id = fm_constants.FM_ALARM_ID_DEVICE_IMAGE_UPDATE_IN_PROGRESS system_uuid = self.dbapi.isystem_get_one().uuid @@ -11843,6 +11837,9 @@ class ConductorManager(service.PeriodicService): service_affecting=False) self.fm_api.set_fault(fault) + def remove_device_image(self, context): + self._clear_device_image_alarm(context) + def host_device_image_update_next(self, context, host_uuid): # Find the first device on this host that needs updating, # and trigger an update of it. @@ -11957,11 +11954,13 @@ class ConductorManager(service.PeriodicService): img.image_uuid)) def _clear_device_image_alarm(self, context): - # If there are no more pending device image update in the DB - # for any host, and if no host has the "reboot needed" DB entry set, - # then the "Device image update in progress" alarm is cleared. + # If there are no more pending, failed or in-progress device image + # update in the DB for any host, and if no host has the "reboot needed" + # DB entry set, then the "Device image update in progress" alarm is cleared. dev_img_list = self.dbapi.device_image_state_get_all( - status=dconstants.DEVICE_IMAGE_UPDATE_PENDING) + status=[dconstants.DEVICE_IMAGE_UPDATE_PENDING, + dconstants.DEVICE_IMAGE_UPDATE_FAILED, + dconstants.DEVICE_IMAGE_UPDATE_IN_PROGRESS]) if not dev_img_list: if self.dbapi.count_hosts_matching_criteria(reboot_needed=True) > 0: return diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index 82856337cc..f8c3cc244b 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -1963,15 +1963,19 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): return self.cast(context, self.make_msg('delete_bitstream_file', filename=filename)) - def apply_device_image(self, context, host_uuid): + def apply_device_image(self, context): """Asynchronously, have the conductor apply the device image - on this host. :param context: request context - :param host_uuid: uuid or id of the host """ - return self.cast(context, self.make_msg('apply_device_image', - host_uuid=host_uuid)) + return self.cast(context, self.make_msg('apply_device_image')) + + def remove_device_image(self, context): + """Asynchronously, have the conductor remove the device image + + :param context: request context + """ + return self.cast(context, self.make_msg('remove_device_image')) def host_device_image_update(self, context, host_uuid): """Asynchronously, have the conductor update the device image diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py index 72c3dfff92..0e9fa48acd 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_device_image.py @@ -28,6 +28,7 @@ class FakeConductorAPI(object): self.store_bitstream_file = mock.MagicMock() self.delete_bitstream_file = mock.MagicMock() self.apply_device_image = mock.MagicMock() + self.remove_device_image = mock.MagicMock() class TestDeviceImage(base.FunctionalTest, dbbase.BaseHostTestCase):