diff --git a/nailgun/nailgun/consts.py b/nailgun/nailgun/consts.py index 7c6e4de2a2..c60b1c2a31 100644 --- a/nailgun/nailgun/consts.py +++ b/nailgun/nailgun/consts.py @@ -259,7 +259,12 @@ TASK_NAMES = Enum( 'deployment', 'provision', 'stop_deployment', + # reset_environment supertask contains three subtasks: + # reset_nodes, remove_keys and remove_ironic_bootstrap 'reset_environment', + 'reset_nodes', + 'remove_keys', + 'remove_ironic_bootstrap', 'update', 'spawn_vms', diff --git a/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_9_2.py b/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_9_2.py index 06e95346e0..69f7359b3a 100644 --- a/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_9_2.py +++ b/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_9_2.py @@ -50,9 +50,11 @@ def upgrade(): upgrade_node_nic_attributes() upgrade_node_bond_attributes() upgrade_tags_set() + upgrade_transaction_names() def downgrade(): + downgrade_transaction_names() downgrade_tags_set() downgrade_node_bond_attributes() downgrade_node_nic_attributes() @@ -250,6 +252,74 @@ FUEL_SECURITY_GROUPS_VERSION = '9.0' # version of Fuel when DPDK hugepages was introduced FUEL_DPDK_HUGEPAGES_VERSION = '9.0' +TASK_NAMES_OLD = ( + 'super', + + # Cluster changes + # For deployment supertask, it contains + # two subtasks deployment and provision + 'deploy', + 'deployment', + 'provision', + 'stop_deployment', + 'reset_environment', + 'update', + 'spawn_vms', + + 'node_deletion', + 'cluster_deletion', + 'remove_images', + 'check_before_deployment', + + # network + 'check_networks', + 'verify_networks', + 'check_dhcp', + 'verify_network_connectivity', + 'multicast_verification', + 'check_repo_availability', + 'check_repo_availability_with_setup', + 'dry_run_deployment', + + # dump + 'dump', + + 'capacity_log', + + # statistics + 'create_stats_user', + 'remove_stats_user', + + # setup dhcp via dnsmasq for multi-node-groups + 'update_dnsmasq' +) + +TASK_NAMES_NEW = TASK_NAMES_OLD + ( + 'reset_nodes', + 'remove_keys', + 'remove_ironic_bootstrap', +) + + +def upgrade_transaction_names(): + migration.upgrade_enum( + 'tasks', + 'name', + 'task_name', + TASK_NAMES_OLD, + TASK_NAMES_NEW + ) + + +def downgrade_transaction_names(): + migration.upgrade_enum( + 'tasks', + 'name', + 'task_name', + TASK_NAMES_NEW, + TASK_NAMES_OLD + ) + def update_vmware_attributes_metadata(upgrade): connection = op.get_bind() diff --git a/nailgun/nailgun/rpc/receiver.py b/nailgun/nailgun/rpc/receiver.py index 994277de46..f4725090ca 100644 --- a/nailgun/nailgun/rpc/receiver.py +++ b/nailgun/nailgun/rpc/receiver.py @@ -796,11 +796,35 @@ class NailgunReceiver(object): process, len(nodes), message) @classmethod - def reset_environment_resp(cls, **kwargs): - logger.info( - "RPC method reset_environment_resp received: %s", - jsonutils.dumps(kwargs) + def _restore_pending_changes(cls, nodes, task, ia_nodes): + task.cluster.status = consts.CLUSTER_STATUSES.new + objects.Cluster.add_pending_changes( + task.cluster, + consts.CLUSTER_CHANGES.attributes ) + objects.Cluster.add_pending_changes( + task.cluster, + consts.CLUSTER_CHANGES.networks + ) + node_uids = [n["uid"] for n in itertools.chain(nodes, ia_nodes)] + q_nodes = objects.NodeCollection.filter_by_id_list(None, node_uids) + q_nodes = objects.NodeCollection.filter_by( + q_nodes, + cluster_id=task.cluster_id + ) + q_nodes = objects.NodeCollection.order_by(q_nodes, 'id') + # locking Nodes for update + update_nodes = objects.NodeCollection.lock_for_update( + q_nodes + ).all() + + for node in update_nodes: + logs_utils.delete_node_logs(node) + objects.Node.reset_to_discover(node) + + @classmethod + def _reset_resp(cls, successful_message, restore_pending_changes=False, + **kwargs): task_uuid = kwargs.get('task_uuid') nodes = kwargs.get('nodes', []) ia_nodes = kwargs.get('inaccessible_nodes', []) @@ -813,59 +837,53 @@ class NailgunReceiver(object): return if status == consts.TASK_STATUSES.ready: - # restoring pending changes - task.cluster.status = consts.CLUSTER_STATUSES.new - objects.Cluster.add_pending_changes( - task.cluster, - consts.CLUSTER_CHANGES.attributes - ) - objects.Cluster.add_pending_changes( - task.cluster, - consts.CLUSTER_CHANGES.networks - ) - - node_uids = [n["uid"] for n in itertools.chain(nodes, ia_nodes)] - q_nodes = objects.NodeCollection.filter_by_id_list(None, node_uids) - q_nodes = objects.NodeCollection.filter_by( - q_nodes, - cluster_id=task.cluster_id - ) - q_nodes = objects.NodeCollection.order_by(q_nodes, 'id') - - # locking Nodes for update - update_nodes = objects.NodeCollection.lock_for_update( - q_nodes - ).all() - - for node in update_nodes: - logs_utils.delete_node_logs(node) - objects.Node.reset_to_discover(node) - + if restore_pending_changes: + cls._restore_pending_changes(nodes, task, ia_nodes) if ia_nodes: cls._notify_inaccessible( task.cluster_id, [n["uid"] for n in ia_nodes], u"environment resetting" ) - - message = ( - u"Environment '{0}' " - u"was successfully reset".format( - task.cluster.name or task.cluster_id - ) + message = successful_message.format( + task.cluster.name or task.cluster_id ) - notifier.notify( "done", message, task.cluster_id ) - data = {'status': status, 'progress': progress, 'message': message} objects.Task.update(task, data) - cls._update_action_log_entry(status, task.name, task_uuid, nodes) + @classmethod + def reset_environment_resp(cls, **kwargs): + logger.info( + "RPC method reset_environment_resp received: %s", + jsonutils.dumps(kwargs) + ) + message = u"Environment '{0}' was successfully reset" + cls._reset_resp(message, restore_pending_changes=True, **kwargs) + + @classmethod + def remove_keys_resp(cls, **kwargs): + logger.info( + "RPC method remove_keys_resp received: %s", + jsonutils.dumps(kwargs) + ) + message = u"Keys were removed from environment '{0}'" + cls._reset_resp(message, **kwargs) + + @classmethod + def remove_ironic_bootstrap_resp(cls, **kwargs): + logger.info( + "RPC method remove_ironic_bootstrap_resp received: %s", + jsonutils.dumps(kwargs) + ) + message = u"Ironic bootstrap was removed from environment '{0}'" + cls._reset_resp(message, **kwargs) + @classmethod def _notify_inaccessible(cls, cluster_id, nodes_uids, action): ia_nodes_db = db().query(Node.name).filter( diff --git a/nailgun/nailgun/task/manager.py b/nailgun/nailgun/task/manager.py index ea42b0d7bf..a905be2aba 100644 --- a/nailgun/nailgun/task/manager.py +++ b/nailgun/nailgun/task/manager.py @@ -904,18 +904,22 @@ class ResetEnvironmentTaskManager(ClearTaskHistory): db().add(supertask) al = TaskHelper.create_action_log(supertask) + reset_nodes = supertask.create_subtask( + consts.TASK_NAMES.reset_nodes + ) + remove_keys_task = supertask.create_subtask( - consts.TASK_NAMES.reset_environment + consts.TASK_NAMES.remove_keys ) remove_ironic_bootstrap_task = supertask.create_subtask( - consts.TASK_NAMES.reset_environment + consts.TASK_NAMES.remove_ironic_bootstrap ) db.commit() rpc.cast('naily', [ - tasks.ResetEnvironmentTask.message(supertask), + tasks.ResetEnvironmentTask.message(reset_nodes), tasks.RemoveIronicBootstrap.message(remove_ironic_bootstrap_task), tasks.RemoveClusterKeys.message(remove_keys_task) ]) diff --git a/nailgun/nailgun/task/task.py b/nailgun/nailgun/task/task.py index aff8603d7c..f2d7db0d53 100644 --- a/nailgun/nailgun/task/task.py +++ b/nailgun/nailgun/task/task.py @@ -1009,7 +1009,7 @@ class RemoveClusterKeys(object): rpc_message = make_astute_message( task, "execute_tasks", - "reset_environment_resp", + "remove_keys_resp", { "tasks": [ tasks_templates.make_shell_task( @@ -1040,7 +1040,7 @@ class RemoveIronicBootstrap(object): rpc_message = make_astute_message( task, "execute_tasks", - "reset_environment_resp", + "remove_ironic_bootstrap_resp", { "tasks": [ tasks_templates.make_shell_task( diff --git a/nailgun/nailgun/test/integration/test_reset_environment.py b/nailgun/nailgun/test/integration/test_reset_environment.py index d0bbafc62b..908be2ea7c 100644 --- a/nailgun/nailgun/test/integration/test_reset_environment.py +++ b/nailgun/nailgun/test/integration/test_reset_environment.py @@ -73,7 +73,11 @@ class TestResetEnvironment(BaseIntegrationTest): cluster_db.id ) tasks_after_reset = list(set([task.name for task in cluster_tasks])) - self.assertEqual(tasks_after_reset, ['reset_environment']) + self.assertItemsEqual(tasks_after_reset, + ['reset_environment', + 'reset_nodes', + 'remove_ironic_bootstrap', + 'remove_keys']) # FIXME(aroma): remove when stop action will be reworked for ha # cluster. To get more details, please, refer to [1] diff --git a/nailgun/nailgun/test/integration/test_rpc_consumer.py b/nailgun/nailgun/test/integration/test_rpc_consumer.py index 8b529a2770..dd5c86b539 100644 --- a/nailgun/nailgun/test/integration/test_rpc_consumer.py +++ b/nailgun/nailgun/test/integration/test_rpc_consumer.py @@ -1725,3 +1725,87 @@ class TestResetEnvironment(BaseReciverTestCase): } self.receiver.reset_environment_resp(**resp) mock_delete_logs.assert_called_once_with(node) + + def test_task_message(self): + cluster = self.env.create( + cluster_kwargs={}, + nodes_kwargs=[ + {"api": False, "status": consts.NODE_STATUSES.ready}, + ] + ) + + node = self.env.nodes[0] + + reset_environment = Task( + uuid=str(uuid.uuid4()), + name=consts.TASK_NAMES.reset_environment, + cluster_id=cluster.id) + self.db.add(reset_environment) + self.db.flush() + + reset_nodes_task = reset_environment.create_subtask( + consts.TASK_NAMES.reset_nodes + ) + remove_keys_task = reset_environment.create_subtask( + consts.TASK_NAMES.remove_keys + ) + remove_ironic_bootstrap_task = ( + reset_environment.create_subtask( + consts.TASK_NAMES.remove_ironic_bootstrap)) + self.db.flush() + + reset_nodes_message = ( + u"Environment '{0}' was successfully reset".format( + reset_nodes_task.cluster.name or + reset_nodes_task.cluster_id + ) + ) + remove_keys_message = ( + u"Keys were removed from environment '{0}'").format( + remove_keys_task.cluster.name or remove_keys_task.cluster_id) + + remove_ironic_bootstrap_message = ( + u"Ironic bootstrap was removed from environment '{0}'").format( + remove_ironic_bootstrap_task.cluster.name or + remove_ironic_bootstrap_task.cluster_id) + + reset_environment_message = ( + u'\n'.join( + (reset_nodes_message, remove_keys_message, + remove_ironic_bootstrap_message) + ) + ) + + reset_environment_resp = { + 'task_uuid': reset_nodes_task.uuid, + 'status': consts.TASK_STATUSES.ready, + 'nodes': [ + {'uid': node.uid}, + ] + } + remove_keys_resp = { + 'task_uuid': remove_keys_task.uuid, + 'status': consts.TASK_STATUSES.ready, + 'nodes': [ + {'uid': node.uid}, + ] + } + remove_ironic_bootstrap_resp = { + 'task_uuid': remove_ironic_bootstrap_task.uuid, + 'status': consts.TASK_STATUSES.ready, + 'nodes': [ + {'uid': node.uid}, + ] + } + self.receiver.reset_environment_resp(**reset_environment_resp) + self.receiver.remove_keys_resp(**remove_keys_resp) + self.receiver.remove_ironic_bootstrap_resp( + **remove_ironic_bootstrap_resp + ) + self.assertEqual(reset_nodes_message, + reset_nodes_task.message) + self.assertEqual(remove_keys_message, remove_keys_task.message) + self.assertEqual(remove_ironic_bootstrap_message, + remove_ironic_bootstrap_task.message) + self.assertEqual(reset_environment_message, + reset_environment.message) diff --git a/nailgun/nailgun/test/unit/test_migration_fuel_9_2.py b/nailgun/nailgun/test/unit/test_migration_fuel_9_2.py index eaa6beb374..e4fe29086c 100644 --- a/nailgun/nailgun/test/unit/test_migration_fuel_9_2.py +++ b/nailgun/nailgun/test/unit/test_migration_fuel_9_2.py @@ -863,3 +863,54 @@ class TestNodeNICAndBondAttributesMigration(base.BaseAlembicMigrationTest): # self.assertNotIn('offloading_modes', bonds_table.c) # self.assertNotIn('interface_properties', bonds_table.c) # self.assertNotIn('bond_properties', bonds_table.c) + + +class TestTransactionsNames(base.BaseAlembicMigrationTest): + + def test_field_reset_environment_exist(self): + db.execute( + self.meta.tables['tasks'].insert(), + [ + { + 'uuid': 'fake_task_uuid_0', + 'name': 'reset_environment', + 'status': 'pending' + } + ] + ) + + def test_field_reset_nodes_exist(self): + db.execute( + self.meta.tables['tasks'].insert(), + [ + { + 'uuid': 'fake_task_uuid_0', + 'name': 'reset_nodes', + 'status': 'pending' + } + ] + ) + + def test_field_remove_keys_exist(self): + db.execute( + self.meta.tables['tasks'].insert(), + [ + { + 'uuid': 'fake_task_uuid_0', + 'name': 'remove_keys', + 'status': 'pending' + } + ] + ) + + def test_field_remove_ironic_bootstrap_exist(self): + db.execute( + self.meta.tables['tasks'].insert(), + [ + { + 'uuid': 'fake_task_uuid_0', + 'name': 'remove_ironic_bootstrap', + 'status': 'pending' + } + ] + )