diff --git a/nailgun/nailgun/api/v1/validators/json_schema/role.py b/nailgun/nailgun/api/v1/validators/json_schema/role.py index 96f9f8a958..91bfa1ccd2 100644 --- a/nailgun/nailgun/api/v1/validators/json_schema/role.py +++ b/nailgun/nailgun/api/v1/validators/json_schema/role.py @@ -103,7 +103,8 @@ ROLE_META_INFO = { "description": ("Name of a role group which reflects the role" " purpose in cloud or deployment process")}, "limits": LIMITS, - "restrictions": base_types.RESTRICTIONS}} + "restrictions": base_types.RESTRICTIONS, + "tags": base_types.STRINGS_ARRAY}} SCHEMA = { diff --git a/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_11_0.py b/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_11_0.py index e4b84cb70a..5b0a320407 100644 --- a/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_11_0.py +++ b/nailgun/nailgun/db/migration/alembic_migrations/versions/fuel_11_0.py @@ -21,6 +21,8 @@ Create Date: 2016-10-22 02:11:47.708895 """ from alembic import op +from oslo_serialization import jsonutils +import six import sqlalchemy as sa @@ -34,9 +36,13 @@ down_revision = 'c6edea552f1e' def upgrade(): upgrade_cluster_roles() + upgrade_tags_meta() + upgrade_primary_unit() def downgrade(): + downgrade_primary_unit() + downgrade_tags_meta() downgrade_cluster_roles() @@ -60,3 +66,81 @@ def upgrade_cluster_roles(): def downgrade_cluster_roles(): op.drop_column('clusters', 'roles_metadata') op.drop_column('clusters', 'volumes_metadata') + + +def upgrade_tags_meta(): + connection = op.get_bind() + op.add_column( + 'releases', + sa.Column('tags_metadata', + fields.JSON(), + server_default='{}', + nullable=False), + ) + op.add_column( + 'clusters', + sa.Column('tags_metadata', + fields.JSON(), + server_default='{}', + nullable=False), + ) + op.add_column( + 'plugins', + sa.Column('tags_metadata', + fields.JSON(), + server_default='{}', + nullable=False), + ) + + q_get_role_meta = "SELECT id, roles_metadata FROM {}" + q_update_tags_meta = ("UPDATE {} SET tags_metadata = :tags_meta " + "WHERE id = :obj_id") + q_update_roles_meta = ("UPDATE {} SET roles_metadata = :roles_meta " + "WHERE id = :obj_id") + + for table in ['releases', 'plugins']: + for obj_id, roles_meta in connection.execute( + sa.text(q_get_role_meta.format(table))): + tags_meta = {} + roles_meta = jsonutils.loads(roles_meta or '{}') + for role_name, meta in six.iteritems(roles_meta): + meta['tags'] = [role_name] + tags_meta[role_name] = {'has_primary': meta.get('has_primary', + False)} + connection.execute(sa.text(q_update_roles_meta.format(table)), + roles_meta=jsonutils.dumps(roles_meta), + obj_id=obj_id) + connection.execute(sa.text(q_update_tags_meta.format(table)), + tags_meta=jsonutils.dumps(tags_meta), + obj_id=obj_id) + + +def downgrade_tags_meta(): + op.drop_column('plugins', 'tags_metadata') + op.drop_column('clusters', 'tags_metadata') + op.drop_column('releases', 'tags_metadata') + + +def upgrade_primary_unit(): + op.alter_column('nodes', 'primary_roles', new_column_name='primary_tags') + + +def downgrade_primary_unit(): + connection = op.get_bind() + q_get_roles = sa.text(''' + SELECT id, roles, pending_roles, primary_tags + FROM nodes + ''') + q_update_primary_tags = sa.text(''' + UPDATE nodes + SET primary_tags = :primary_tags + WHERE id = :node_id + ''') + for node_id, roles, p_roles, pr_tags in connection.execute(q_get_roles): + primary_tags = list(set(roles + p_roles) & set(pr_tags)) + connection.execute( + q_update_primary_tags, + node_id=node_id, + primary_tags=primary_tags + ) + op.alter_column('nodes', 'primary_tags', new_column_name='primary_roles') diff --git a/nailgun/nailgun/db/sqlalchemy/models/cluster.py b/nailgun/nailgun/db/sqlalchemy/models/cluster.py index 3511819125..06175dd511 100644 --- a/nailgun/nailgun/db/sqlalchemy/models/cluster.py +++ b/nailgun/nailgun/db/sqlalchemy/models/cluster.py @@ -127,6 +127,9 @@ class Cluster(Base): roles_metadata = Column(MutableDict.as_mutable(JSON), default={}, server_default='{}') + tags_metadata = Column(MutableDict.as_mutable(JSON), + server_default='{}', + nullable=False) @property def changes(self): diff --git a/nailgun/nailgun/db/sqlalchemy/models/node.py b/nailgun/nailgun/db/sqlalchemy/models/node.py index 731ab5b501..35c18fc4ab 100644 --- a/nailgun/nailgun/db/sqlalchemy/models/node.py +++ b/nailgun/nailgun/db/sqlalchemy/models/node.py @@ -99,8 +99,8 @@ class Node(Base): default=[], nullable=False, server_default='{}') pending_roles = Column(psql.ARRAY(String(consts.ROLE_NAME_MAX_SIZE)), default=[], nullable=False, server_default='{}') - primary_roles = Column(psql.ARRAY(String(consts.ROLE_NAME_MAX_SIZE)), - default=[], nullable=False, server_default='{}') + primary_tags = Column(psql.ARRAY(String(consts.ROLE_NAME_MAX_SIZE)), + default=[], nullable=False, server_default='{}') nic_interfaces = relationship("NodeNICInterface", backref="node", cascade="all, delete-orphan", diff --git a/nailgun/nailgun/db/sqlalchemy/models/plugins.py b/nailgun/nailgun/db/sqlalchemy/models/plugins.py index b2037a6470..9489a0862f 100644 --- a/nailgun/nailgun/db/sqlalchemy/models/plugins.py +++ b/nailgun/nailgun/db/sqlalchemy/models/plugins.py @@ -180,6 +180,8 @@ class Plugin(Base): MutableDict.as_mutable(JSON), server_default='{}', nullable=False) roles_metadata = Column( MutableDict.as_mutable(JSON), server_default='{}', nullable=False) + tags_metadata = Column( + MutableDict.as_mutable(JSON), server_default='{}', nullable=False) network_roles_metadata = Column( MutableList.as_mutable(JSON), server_default='[]', nullable=False) nic_attributes_metadata = Column( diff --git a/nailgun/nailgun/db/sqlalchemy/models/release.py b/nailgun/nailgun/db/sqlalchemy/models/release.py index b5437da365..cb3feb61fe 100644 --- a/nailgun/nailgun/db/sqlalchemy/models/release.py +++ b/nailgun/nailgun/db/sqlalchemy/models/release.py @@ -54,6 +54,8 @@ class Release(Base): volumes_metadata = Column(MutableDict.as_mutable(JSON), default={}) modes_metadata = Column(MutableDict.as_mutable(JSON), default={}) roles_metadata = Column(MutableDict.as_mutable(JSON), default={}) + tags_metadata = Column( + MutableDict.as_mutable(JSON), server_default='{}', nullable=False) network_roles_metadata = Column( MutableList.as_mutable(JSON), default=[], server_default='[]') vmware_attributes_metadata = Column( diff --git a/nailgun/nailgun/extensions/network_manager/serializers/neutron_serializers.py b/nailgun/nailgun/extensions/network_manager/serializers/neutron_serializers.py index 7f603236b4..29ff38bbd4 100644 --- a/nailgun/nailgun/extensions/network_manager/serializers/neutron_serializers.py +++ b/nailgun/nailgun/extensions/network_manager/serializers/neutron_serializers.py @@ -1074,7 +1074,7 @@ class NeutronNetworkDeploymentSerializer70( for node in objects.Cluster.get_nodes_not_for_deletion(cluster): name = objects.Node.get_slave_name(node) - node_roles = objects.Node.all_roles(node) + node_roles = objects.Node.all_tags(node) network_roles = cls.get_network_role_mapping_to_ip(node) # Use permanent identifier as a node key key = objects.Node.permanent_id(node) diff --git a/nailgun/nailgun/extensions/network_manager/serializers/nova_serializers.py b/nailgun/nailgun/extensions/network_manager/serializers/nova_serializers.py index 9e56f6f875..91ff86bdc4 100644 --- a/nailgun/nailgun/extensions/network_manager/serializers/nova_serializers.py +++ b/nailgun/nailgun/extensions/network_manager/serializers/nova_serializers.py @@ -365,7 +365,7 @@ class NovaNetworkDeploymentSerializer70(NovaNetworkDeploymentSerializer61): for n in Cluster.get_nodes_not_for_deletion(cluster): name = Node.get_slave_name(n) - node_roles = Node.all_roles(n) + node_roles = Node.all_tags(n) # Use permanent identifier as a node key key = Node.permanent_id(n) diff --git a/nailgun/nailgun/fixtures/openstack.yaml b/nailgun/nailgun/fixtures/openstack.yaml index 78fe99973f..b674ae5807 100644 --- a/nailgun/nailgun/fixtures/openstack.yaml +++ b/nailgun/nailgun/fixtures/openstack.yaml @@ -16,7 +16,6 @@ update_required: - compute - cinder - has_primary: true public_ip_required: true public_for_dvr_required: true group: "base" @@ -27,6 +26,8 @@ - condition: "settings:neutron_advanced_configuration.neutron_l3_ha.value == true" min: 2 message: "Neutron L3 HA requires at least 2 Controller nodes to function properly." + tags: + - controller compute: name: "Compute" description: "A Compute node creates, manages, and terminates virtual machine instances." @@ -36,6 +37,8 @@ limits: recommended: 1 fault_tolerance: "2%" + tags: + - compute cinder: # NOTE: naming, see https://bugs.launchpad.net/fuel/+bug/1383224 name: "Cinder" @@ -50,10 +53,11 @@ # NOTE: https://bugs.launchpad.net/fuel/+bug/1372914 - Prohibit possibility of adding cinder nodes to an environment with Ceph RBD - condition: "settings:storage.volumes_ceph.value == true" message: "Ceph RBD cannot be used with Cinder" + tags: + - cinder cinder-block-device: name: 'Cinder Block Device' description: 'Host node for Cinder Block Devices.' - has_primary: false public_ip_required: false weight: 35 group: "storage" @@ -67,6 +71,8 @@ message: "Cinder Block Device driver should be enabled in the environment settings." - condition: "settings:storage.volumes_ceph.value == true" message: "Ceph RBD cannot be used with Cinder Block Device" + tags: + - cinder-block-device cinder-vmware: name: "Cinder Proxy to VMware Datastore" description: "Cinder-VMware provides scheduling of block storage resources delivered over VMware vCenter. Block storage can be used for database storage, expandable file systems, or providing a server with access to raw block level devices." @@ -78,6 +84,8 @@ - condition: "settings:common.use_vcenter.value == false" action: "hide" message: "VMware vCenter not enabled for cluster" + tags: + - cinder-vmware ceph-osd: name: "Ceph OSD" description: "Ceph storage can be configured to provide storage for block volumes (Cinder), images (Glance) and ephemeral instance storage (Nova). It can also provide object storage through the S3 and Swift API (See settings to enable each)." @@ -90,6 +98,8 @@ message: "Ceph should be enabled in the environment settings." update_once: - controller + tags: + - ceph-osd mongo: name: "Telemetry - MongoDB" description: "A feature-complete and recommended database for storage of metering data from OpenStack Telemetry (Ceilometer)." @@ -98,7 +108,6 @@ conflicts: - compute - ceph-osd - has_primary: true limits: min: 1 overrides: @@ -113,11 +122,15 @@ message: "Ceilometer should be enabled in the environment settings." - condition: "settings:additional_components.mongo.value == true" message: "You are already using external MongoDB." + tags: + - mongo base-os: name: "Operating System" description: "Install base Operating System without additional packages and configuration." weight: 70 group: "other" + tags: + - base-os virt: name: "Virtual" description: "ADVANCED: Make available possibilities to spawn vms on this node that can be assign as a normal nodes." @@ -131,6 +144,8 @@ - condition: "not ('advanced' in version:feature_groups)" action: "hide" message: "Advanced feature should be enabled in feature groups" + tags: + - virt compute-vmware: name: "Compute VMware" description: "A node that runs nova-compute with VCDriver, that manages ESXi computing resources via VMware vCenter." @@ -150,6 +165,8 @@ - condition: "settings:common.use_vcenter.value == false" action: "hide" message: "VMware vCenter not enabled for cluster" + tags: + - compute-vmware ironic: name: "Ironic" description: "Ironic conductor." @@ -163,6 +180,31 @@ restrictions: - condition: "settings:additional_components.ironic.value == false" message: "Ironic should be enabled in the environment settings." + tags: + - ironic + tags_metadata: + controller: + has_primary: true + compute: + has_primary: false + cinder: + has_primary: false + cinder-block-device: + has_primary: false + cinder-vmware: + has_primary: false + ceph-osd: + has_primary: false + mongo: + has_primary: true + base-os: + has_primary: false + virt: + has_primary: false + compute-vmware: + has_primary: false + ironic: + has_primary: false network_roles_metadata: - diff --git a/nailgun/nailgun/lcm/transaction_serializer.py b/nailgun/nailgun/lcm/transaction_serializer.py index a612055ec3..c02146b6bb 100644 --- a/nailgun/nailgun/lcm/transaction_serializer.py +++ b/nailgun/nailgun/lcm/transaction_serializer.py @@ -24,7 +24,7 @@ from nailgun import errors from nailgun.lcm.task_serializer import TasksSerializersFactory from nailgun.logger import logger from nailgun.settings import settings -from nailgun.utils.role_resolver import NameMatchingPolicy +from nailgun.utils.resolvers import NameMatchingPolicy # This class has similar functional with TasksSerializer from task deploy @@ -153,8 +153,8 @@ class TransactionSerializer(object): consts.ORCHESTRATOR_TASK_TYPES.skipped ) - def __init__(self, context, role_resolver): - self.role_resolver = role_resolver + def __init__(self, context, resolver): + self.resolver = resolver self.context = context self.tasks_graph = {} self.tasks_dictionary = {} @@ -165,15 +165,15 @@ class TransactionSerializer(object): self.concurrency_policy = get_concurrency_policy() @classmethod - def serialize(cls, context, tasks, role_resolver): + def serialize(cls, context, tasks, resolver): """Resolves roles and dependencies for tasks. :param context: the deployment context :param tasks: the deployment tasks - :param role_resolver: the nodes role resolver + :param resolver: the nodes tag resolver :return: the list of serialized task per node """ - serializer = cls(context, role_resolver) + serializer = cls(context, resolver) serializer.process_tasks(tasks) serializer.resolve_dependencies() tasks_graph = serializer.tasks_graph @@ -248,8 +248,8 @@ class TransactionSerializer(object): yield node_id, task for task in groups: - node_ids = self.role_resolver.resolve( - task.get('roles', task.get('groups')) + node_ids = self.resolver.resolve( + task.get('tags', task.get('roles', task.get('groups'))) ) if not node_ids: continue @@ -279,8 +279,8 @@ class TransactionSerializer(object): # all synchronisation tasks will run on sync node return [None] # TODO(bgaifullin) remove deprecated groups - return self.role_resolver.resolve( - task.get('roles', task.get('groups')) + return self.resolver.resolve( + task.get('tags', task.get('roles', task.get('groups'))) ) def resolve_dependencies(self): @@ -338,7 +338,7 @@ class TransactionSerializer(object): return for dep in six.moves.filter(None, dependencies): - roles = dep.get('role', consts.TASK_ROLES.all) + roles = dep.get('tags', dep.get('role', consts.TASK_ROLES.all)) if roles == consts.TASK_ROLES.self: node_ids = [node_id] @@ -347,7 +347,7 @@ class TransactionSerializer(object): node_ids = [None] excludes = [] else: - node_ids = self.role_resolver.resolve( + node_ids = self.resolver.resolve( roles, dep.get('policy', consts.NODE_RESOLVE_POLICY.all) ) excludes = [(task_id, node_id)] diff --git a/nailgun/nailgun/objects/cluster.py b/nailgun/nailgun/objects/cluster.py index 8ddd57c167..8bcc2872f2 100644 --- a/nailgun/nailgun/objects/cluster.py +++ b/nailgun/nailgun/objects/cluster.py @@ -825,36 +825,22 @@ class Cluster(NailgunObject): return instance.roles_metadata @classmethod - def set_primary_role(cls, instance, nodes, role_name): - """Method for assigning primary attribute for specific role. - - - verify that there is no primary attribute of specific role - assigned to cluster nodes with this role in role list - or pending role list, and this node is not marked for deletion - - if there is no primary role assigned, filter nodes which have current - role in roles or pending_roles - - if there is nodes with ready state - they should have higher priority - - if role was in primary_role_list - change primary attribute - for that association, same for role_list, this is required - because deployment_serializer used by cli to generate deployment info + def set_primary_tag(cls, instance, nodes, tag): + """Method for assigning primary attribute for specific tag. :param instance: Cluster db objects :param nodes: list of Node db objects - :param role_name: string with known role name + :param tag: string with known tag name """ - if role_name not in cls.get_roles(instance): - logger.warning( - 'Trying to assign primary for non-existing role %s', role_name) - return - - node = cls.get_primary_node(instance, role_name) + from objects import Node + node = cls.get_primary_node(instance, tag) if not node: # get nodes with a given role name which are not going to be # removed filtered_nodes = [] for node in nodes: if (not node.pending_deletion and ( - role_name in set(node.roles + node.pending_roles))): + tag in Node.get_tags(node))): filtered_nodes.append(node) filtered_nodes = sorted(filtered_nodes, key=lambda node: node.id) @@ -864,27 +850,29 @@ class Cluster(NailgunObject): if node.status == consts.NODE_STATUSES.ready), filtered_nodes[0]) - primary_node.primary_roles = list(primary_node.primary_roles) - primary_node.primary_roles.append(role_name) + primary_node.primary_tags = list(primary_node.primary_tags) + primary_node.primary_tags.append(tag) db().flush() @classmethod - def set_primary_roles(cls, instance, nodes): - """Assignment of all primary attribute for all roles that requires it. + def set_primary_tags(cls, instance, nodes): + """Assignment of all primary attribute for all tags that requires it. This method is idempotent - To mark role as primary add has_primary: true attribute to release + To mark tag as primary add "has_primary: true" attribute to tag meta :param instance: Cluster db object :param nodes: list of Node db objects """ if not instance.is_ha_mode: return - roles_metadata = cls.get_roles(instance) - for role, meta in six.iteritems(roles_metadata): - if meta.get('has_primary'): - cls.set_primary_role(instance, nodes, role) + + tags_meta = cls.get_tags_metadata(instance) + for role, meta in six.iteritems(cls.get_roles(instance)): + for tag in meta.get('tags', []): + if tags_meta[tag].get('has_primary'): + cls.set_primary_tag(instance, nodes, tag) @classmethod def get_nodes_by_role(cls, instance, role_name): @@ -925,36 +913,31 @@ class Cluster(NailgunObject): return query @classmethod - def get_primary_node(cls, instance, role_name): - """Get primary node for role_name + def get_primary_node(cls, instance, tag): + """Get primary node for tag If primary node is not found None will be returned - Pending roles and roles are used in search :param instance: cluster db object :type: python object - :param role_name: node role name + :param tag: node tag name :type: string :returns: node db object or None """ - logger.debug("Getting primary node for role: %s", role_name) - - if role_name not in cls.get_roles(instance): - logger.debug("Role not found: %s", role_name) - return None + logger.debug("Getting primary node for tag: %s", tag) primary_node = db().query(models.Node).filter_by( pending_deletion=False, cluster_id=instance.id ).filter( - models.Node.primary_roles.any(role_name) + models.Node.primary_tags.any(tag) ).first() if primary_node is None: - logger.debug("Not found primary node for role: %s", role_name) + logger.debug("Not found primary node for tag: %s", tag) else: - logger.debug("Found primary node: %s for role: %s", - primary_node.id, role_name) + logger.debug("Found primary node: %s for tag: %s", + primary_node.id, tag) return primary_node @classmethod @@ -1234,6 +1217,26 @@ class Cluster(NailgunObject): tasks.append(task) return tasks + @classmethod + def get_tags_metadata(cls, instance): + """Return proper tags metadata for cluster + + Metadata consists of general tags metadata from release, + tags metadata from cluster and tags metadata from + plugins which are enabled for this cluster. + + :param instance: Cluster DB instance + :returns: dict -- object with merged tags metadata + """ + tags_meta = dict(instance.release.tags_metadata) + cluster_tags_meta = instance.tags_metadata + tags_meta.update(cluster_tags_meta) + + plugins_tags_meta = PluginManager.get_tags_metadata(instance) + tags_meta.update(plugins_tags_meta) + + return tags_meta + @classmethod def get_volumes_metadata(cls, instance): """Return proper volumes metadata for cluster diff --git a/nailgun/nailgun/objects/node.py b/nailgun/nailgun/objects/node.py index a745184180..3e3d67b5b4 100644 --- a/nailgun/nailgun/objects/node.py +++ b/nailgun/nailgun/objects/node.py @@ -329,7 +329,7 @@ class Node(NailgunObject): roles = data.pop("roles", None) pending_roles = data.pop("pending_roles", None) - primary_roles = data.pop("primary_roles", None) + primary_tags = data.pop("primary_tags", None) new_node_meta = data.pop("meta", {}) new_node_cluster_id = data.pop("cluster_id", None) @@ -354,8 +354,8 @@ class Node(NailgunObject): cls.update_roles(new_node, roles) if pending_roles is not None: cls.update_pending_roles(new_node, pending_roles) - if primary_roles is not None: - cls.update_primary_roles(new_node, primary_roles) + if primary_tags is not None: + cls.update_primary_tags(new_node, primary_tags) # adding node into cluster if new_node_cluster_id: @@ -759,7 +759,7 @@ class Node(NailgunObject): instance.group_id = None instance.kernel_params = None cls.update_roles(instance, roles) - cls.update_primary_roles(instance, []) + cls.update_primary_tags(instance, []) cls.update_pending_roles(instance, pending_roles) cls.remove_replaced_params(instance) cls.assign_group(instance) @@ -911,38 +911,38 @@ class Node(NailgunObject): db().flush() @classmethod - def update_primary_roles(cls, instance, new_primary_roles): - """Update primary_roles for Node instance. + def update_primary_tags(cls, instance, new_primary_tags): + """Update primary_tags for Node instance. Logs an error if node doesn't belong to Cluster :param instance: Node instance - :param new_primary_roles: list of new pending role names + :param new_primary_tags: list of new primary tag names :returns: None """ if not instance.cluster_id: logger.warning( - u"Attempting to assign pending roles to node " + u"Attempting to assign peimary tags to node " u"'{0}' which isn't added to cluster".format( instance.full_name)) return - assigned_roles = set(instance.roles + instance.pending_roles) - for role in new_primary_roles: - if role not in assigned_roles: - logger.warning( - u"Could not mark node {0} as primary for {1} role, " - u"because there's no assigned {1} role.".format( - instance.full_name, role) - ) - return + assigned_tags = set(cls.get_tags(instance)) + missing_tags = set(new_primary_tags) - set(assigned_tags) + if missing_tags: + logger.warning( + u"Could not mark node {0} as primary for {1} tags, " + u"because corresponding roles are not assigned.".format( + instance.full_name, missing_tags) + ) + return logger.debug( - u"Updating primary roles for node {0}: {1}".format( + u"Updating primary tags for node {0}: {1}".format( instance.full_name, - new_primary_roles)) + new_primary_tags)) - instance.primary_roles = new_primary_roles + instance.primary_tags = new_primary_tags db().flush() @classmethod @@ -1029,7 +1029,7 @@ class Node(NailgunObject): instance.cluster_id = None instance.group_id = None instance.kernel_params = None - instance.primary_roles = [] + instance.primary_tags = [] instance.hostname = cls.default_slave_name(instance) instance.attributes = {} @@ -1044,7 +1044,7 @@ class Node(NailgunObject): """Move roles to pending_roles""" instance.pending_roles = instance.pending_roles + instance.roles instance.roles = [] - instance.primary_roles = [] + instance.primary_tags = [] db().flush() @classmethod @@ -1113,14 +1113,35 @@ class Node(NailgunObject): instance.network_template = None @classmethod - def all_roles(cls, instance): + def get_tags(cls, instance): + """Get node tags + + Returns list of nodes tags based on roles assigned to it. + Primary tags are included into result as it is. + """ + roles = set(instance.roles + instance.pending_roles) - roles -= set(instance.primary_roles) + roles_meta = Cluster.get_roles(instance.cluster) + tags = () + for role in roles: + tags = itertools.chain(tags, roles_meta[role].get('tags', [role])) + return tags - primary_roles = set([ - 'primary-{0}'.format(role) for role in instance.primary_roles]) + @classmethod + def all_tags(cls, instance): + """Get node tags including primary-tags with prefix - return sorted(roles | primary_roles) + Returns list of nodes tags based on roles assigned to it. + Primary tags are included into result with 'primary-' prefix. + This method is mostly used for node's tasks resolution. + """ + + tags = set(cls.get_tags(instance)) + tags -= set(instance.primary_tags) + primary_tags = set([ + 'primary-{0}'.format(tag) for tag in instance.primary_tags]) + + return sorted(tags | primary_tags) @classmethod def apply_network_template(cls, instance, template): diff --git a/nailgun/nailgun/objects/release.py b/nailgun/nailgun/objects/release.py index 7725818d7c..a0c387ff60 100644 --- a/nailgun/nailgun/objects/release.py +++ b/nailgun/nailgun/objects/release.py @@ -74,6 +74,7 @@ class Release(NailgunObject): for graph_type, graph_data in six.iteritems(graphs): DeploymentGraph.create_for_model( graph_data, release_obj, graph_type) + return release_obj @classmethod diff --git a/nailgun/nailgun/orchestrator/deployment_serializers.py b/nailgun/nailgun/orchestrator/deployment_serializers.py index ab97c44633..ed237a4146 100644 --- a/nailgun/nailgun/orchestrator/deployment_serializers.py +++ b/nailgun/nailgun/orchestrator/deployment_serializers.py @@ -28,8 +28,8 @@ from nailgun import objects from nailgun import plugins from nailgun.settings import settings from nailgun import utils -from nailgun.utils.role_resolver import NameMatchingPolicy -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import NameMatchingPolicy +from nailgun.utils.resolvers import TagResolver from nailgun.orchestrator.base_serializers import MuranoMetadataSerializerMixin from nailgun.orchestrator.base_serializers import \ @@ -51,17 +51,17 @@ class DeploymentMultinodeSerializer(object): def __init__(self, tasks_graph=None): self.task_graph = tasks_graph self.all_nodes = None - self.role_resolver = None + self.resolver = None self.initialized = None def initialize(self, cluster): self.all_nodes = objects.Cluster.get_nodes_not_for_deletion(cluster) - self.role_resolver = RoleResolver(self.all_nodes) + self.resolver = TagResolver(self.all_nodes) self.initialized = cluster.id def finalize(self): self.all_nodes = None - self.role_resolver = None + self.resolver = None self.initialized = None def _ensure_initialized_for(self, cluster): @@ -162,7 +162,7 @@ class DeploymentMultinodeSerializer(object): username = attrs['workloads_collector'].pop('user', None) attrs['workloads_collector']['username'] = username - if self.role_resolver.resolve(['cinder']): + if self.resolver.resolve(['cinder']): attrs['use_cinder'] = True net_serializer = self.get_net_provider_serializer(cluster) @@ -178,7 +178,7 @@ class DeploymentMultinodeSerializer(object): node_list = [] for node in nodes: - for role in objects.Node.all_roles(node): + for role in objects.Node.all_tags(node): node_list.append(cls.serialize_node_for_node_list(node, role)) return node_list @@ -207,7 +207,7 @@ class DeploymentMultinodeSerializer(object): """ serialized_nodes = [] for node in nodes: - for role in objects.Node.all_roles(node): + for role in objects.Node.all_tags(node): serialized_nodes.append( self.serialize_node(node, role) ) @@ -728,7 +728,7 @@ class DeploymentLCMSerializer(DeploymentHASerializer90): def serialize_nodes(self, nodes): serialized_nodes = [] for node in nodes: - roles = objects.Node.all_roles(node) + roles = objects.Node.all_tags(node) if roles: serialized_nodes.append( self.serialize_node(node, roles) @@ -893,7 +893,7 @@ def _invoke_serializer(serializer, cluster, nodes, cluster, cluster.nodes, ignore_customized ) - objects.Cluster.set_primary_roles(cluster, nodes) + objects.Cluster.set_primary_tags(cluster, nodes) return serializer.serialize( cluster, nodes, ignore_customized=ignore_customized, skip_extensions=skip_extensions diff --git a/nailgun/nailgun/orchestrator/orchestrator_graph.py b/nailgun/nailgun/orchestrator/orchestrator_graph.py index 0821296b8b..8810bdc8a2 100644 --- a/nailgun/nailgun/orchestrator/orchestrator_graph.py +++ b/nailgun/nailgun/orchestrator/orchestrator_graph.py @@ -30,7 +30,7 @@ from nailgun import objects from nailgun.orchestrator import priority_serializers as ps from nailgun.orchestrator.tasks_serializer import TaskSerializers from nailgun.policy.name_match import NameMatchingPolicy -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import TagResolver class GraphSolver(nx.DiGraph): @@ -432,7 +432,7 @@ class AstuteGraph(object): :param nodes: list of node db objects """ serialized = [] - role_resolver = RoleResolver(nodes) + resolver = TagResolver(nodes) for task in tasks: @@ -440,7 +440,7 @@ class AstuteGraph(object): continue serializer = self.serializers.get_stage_serializer(task)( - task, self.cluster, nodes, role_resolver=role_resolver) + task, self.cluster, nodes, resolver=resolver) if not serializer.should_execute(): continue diff --git a/nailgun/nailgun/orchestrator/plugins_serializers.py b/nailgun/nailgun/orchestrator/plugins_serializers.py index c6cd740ebf..5f8967b0b1 100644 --- a/nailgun/nailgun/orchestrator/plugins_serializers.py +++ b/nailgun/nailgun/orchestrator/plugins_serializers.py @@ -25,7 +25,7 @@ from nailgun import errors from nailgun.logger import logger import nailgun.orchestrator.tasks_templates as templates from nailgun.settings import settings -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import TagResolver # TODO(bgaifullin) HUCK to prevent cycle imports from nailgun.plugins.manager import PluginManager @@ -34,17 +34,17 @@ from nailgun.plugins.manager import PluginManager class BasePluginDeploymentHooksSerializer(object): # TODO(dshulyak) refactor it to be consistent with task_serializer - def __init__(self, cluster, nodes, role_resolver=None): + def __init__(self, cluster, nodes, resolver=None): """Initialises. :param cluster: the cluster object instance :param nodes: the list of nodes for deployment - :param role_resolver: the instance of BaseRoleResolver + :param resolver: the instance of BaseRoleResolver """ self.cluster = cluster self.nodes = nodes - self.role_resolver = role_resolver or RoleResolver(nodes) + self.resolver = resolver or TagResolver(nodes) def deployment_tasks(self, plugins, stage): plugin_tasks = [] @@ -58,7 +58,7 @@ class BasePluginDeploymentHooksSerializer(object): sorted_tasks = self._sort_by_stage_postfix(plugin_tasks) for task in sorted_tasks: make_task = None - uids = self.role_resolver.resolve(task['role']) + uids = self.resolver.resolve(task.get('tags', task['role'])) if not uids: continue @@ -163,10 +163,10 @@ class PluginsPreDeploymentHooksSerializer(BasePluginDeploymentHooksSerializer): for task in tasks_to_process: # plugin tasks may store information about node # role not only in `role` key but also in `groups` - task_role = task.get('role', task.get('groups')) + task_role = task.get('tags', task.get('role', task.get('groups'))) if task_role == consts.TASK_ROLES.all: # just return all nodes - return self.role_resolver.resolve(consts.TASK_ROLES.all) + return self.resolver.resolve(consts.TASK_ROLES.all) elif isinstance(task_role, six.string_types): roles.add(task_role) elif isinstance(task_role, (list, tuple)): @@ -187,7 +187,7 @@ class PluginsPreDeploymentHooksSerializer(BasePluginDeploymentHooksSerializer): # executes `apt-get update` which fails on CentOS roles.discard(consts.TASK_ROLES.master) - return list(self.role_resolver.resolve(roles)) + return list(self.resolver.resolve(roles)) def create_repositories(self, plugins): operating_system = self.cluster.release.operating_system diff --git a/nailgun/nailgun/orchestrator/stages.py b/nailgun/nailgun/orchestrator/stages.py index b95cbb5b5f..f3bd40411d 100644 --- a/nailgun/nailgun/orchestrator/stages.py +++ b/nailgun/nailgun/orchestrator/stages.py @@ -36,16 +36,16 @@ def stage_serialize(serializer, graph_tasks): def pre_deployment_serialize(orchestrator_graph, cluster, nodes, - role_resolver=None): + resolver=None): graph_tasks = orchestrator_graph.pre_tasks_serialize(nodes) return stage_serialize( plugins_serializers.PluginsPreDeploymentHooksSerializer( - cluster, nodes, role_resolver=role_resolver), graph_tasks) + cluster, nodes, resolver=resolver), graph_tasks) def post_deployment_serialize(orchestrator_graph, cluster, nodes, - role_resolver=None): + resolver=None): graph_tasks = orchestrator_graph.post_tasks_serialize(nodes) return stage_serialize( plugins_serializers.PluginsPostDeploymentHooksSerializer( - cluster, nodes, role_resolver=role_resolver), graph_tasks) + cluster, nodes, resolver=resolver), graph_tasks) diff --git a/nailgun/nailgun/orchestrator/task_based_deployment.py b/nailgun/nailgun/orchestrator/task_based_deployment.py index 7d1fee2c9b..89030e951a 100644 --- a/nailgun/nailgun/orchestrator/task_based_deployment.py +++ b/nailgun/nailgun/orchestrator/task_based_deployment.py @@ -29,9 +29,9 @@ from nailgun.orchestrator.tasks_serializer import CreateVMsOnCompute from nailgun.orchestrator.tasks_serializer import StandardConfigRolesHook from nailgun.orchestrator.tasks_serializer import TaskSerializers from nailgun.orchestrator.tasks_templates import make_noop_task -from nailgun.utils.role_resolver import NameMatchingPolicy -from nailgun.utils.role_resolver import NullResolver -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import NameMatchingPolicy +from nailgun.utils.resolvers import NullResolver +from nailgun.utils.resolvers import TagResolver class NoopSerializer(StandardConfigRolesHook): @@ -40,11 +40,12 @@ class NoopSerializer(StandardConfigRolesHook): return True def get_uids(self): - roles = self.task.get('groups', self.task.get('role')) - if roles is None: + tags = self.task.get('tags', self.task.get('groups', + self.task.get('role'))) + if tags is None: # it means that task is not associated with any node return [None] - return self.role_resolver.resolve(roles) + return self.resolver.resolve(tags) def serialize(self): uids = self.get_uids() @@ -60,7 +61,7 @@ class PluginTaskSerializer(StandardConfigRolesHook): def serialize(self): serializer = self.serializer_class( - self.cluster, self.nodes, role_resolver=self.role_resolver + self.cluster, self.nodes, resolver=self.resolver ) return itertools.chain( serializer.serialize_begin_tasks(), @@ -417,7 +418,7 @@ class TasksSerializer(object): self.deployment_nodes = nodes self.affected_node_ids = frozenset() self.cluster = cluster - self.role_resolver = RoleResolver(self.deployment_nodes) + self.resolver = TagResolver(self.deployment_nodes) self.task_serializer = DeployTaskSerializer() self.task_processor = TaskProcessor() self.tasks_connections = collections.defaultdict(dict) @@ -466,17 +467,17 @@ class TasksSerializer(object): else: tasks_mapping[task['id']] = task skip = not self.task_filter(task['id']) - self.process_task(task, self.role_resolver, skip) + self.process_task(task, self.resolver, skip) self.expand_task_groups(groups, tasks_mapping) # make sure that null node is present self.tasks_connections.setdefault(None, dict()) - def process_task(self, task, role_resolver, skip=False): + def process_task(self, task, resolver, skip=False): """Processes one task one nodes of cluster. :param task: the task instance - :param role_resolver: the role resolver + :param resolver: the role resolver :param skip: make the task as skipped """ @@ -485,7 +486,7 @@ class TasksSerializer(object): ) task_serializer = serializer_factory( task, self.cluster, self.deployment_nodes, - role_resolver=role_resolver + resolver=resolver ) skipped = skip or not task_serializer.should_execute() force = self.events and self.events.check_subscription(task) @@ -493,12 +494,12 @@ class TasksSerializer(object): # Do not call real serializer if it should be skipped task_serializer = NoopSerializer( task, self.cluster, self.deployment_nodes, - role_resolver=role_resolver + resolver=resolver ) - serialised_tasks = self.task_processor.process_tasks( task, task_serializer.serialize() ) + for serialized in serialised_tasks: # all skipped task shall have type skipped # do not exclude them from graph to keep connections between nodes @@ -576,7 +577,8 @@ class TasksSerializer(object): """ for task in groups: skipped = not self.task_filter(task['id']) - node_ids = self.role_resolver.resolve(task.get('role', ())) + node_ids = self.resolver.resolve(task.get('tags', + task.get('role', ()))) for sub_task_id in task.get('tasks', ()): try: sub_task = task_mapping[sub_task_id] @@ -624,13 +626,13 @@ class TasksSerializer(object): return for dep in dependencies: - roles = dep.get('role', consts.TASK_ROLES.all) + roles = dep.get('tags', dep.get('role', consts.TASK_ROLES.all)) if roles == consts.TASK_ROLES.self: node_ids = [node_id] excludes = [] else: - node_ids = self.role_resolver.resolve( + node_ids = self.resolver.resolve( roles, dep.get('policy', consts.NODE_RESOLVE_POLICY.all) ) excludes = [(node_id, task_id)] diff --git a/nailgun/nailgun/orchestrator/tasks_serializer.py b/nailgun/nailgun/orchestrator/tasks_serializer.py index 3c8dab8d40..269aadd674 100644 --- a/nailgun/nailgun/orchestrator/tasks_serializer.py +++ b/nailgun/nailgun/orchestrator/tasks_serializer.py @@ -28,7 +28,7 @@ from nailgun.orchestrator import deployment_serializers from nailgun.orchestrator import tasks_templates as templates from nailgun.settings import settings from nailgun import utils -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import TagResolver @six.add_metaclass(abc.ABCMeta) @@ -89,14 +89,15 @@ class PuppetHook(GenericNodeHook): class StandardConfigRolesHook(ExpressionBasedTask): """Role hooks that serializes task based on config file only.""" - def __init__(self, task, cluster, nodes, role_resolver=None): + def __init__(self, task, cluster, nodes, resolver=None): super(StandardConfigRolesHook, self).__init__(task, cluster) self.nodes = nodes - self.role_resolver = role_resolver or RoleResolver(nodes) + self.resolver = resolver or TagResolver(nodes) def get_uids(self): - return list(self.role_resolver.resolve( - self.task.get('role', self.task.get('groups')) + return list(self.resolver.resolve( + self.task.get('tags', self.task.get('role', + self.task.get('groups'))) )) def serialize(self): @@ -115,7 +116,7 @@ class UploadMOSRepo(GenericRolesHook): identity = 'upload_core_repos' def get_uids(self): - return list(self.role_resolver.resolve(consts.TASK_ROLES.all)) + return list(self.resolver.resolve(consts.TASK_ROLES.all)) def serialize(self): uids = self.get_uids() @@ -161,7 +162,7 @@ class RsyncPuppet(GenericRolesHook): identity = 'rsync_core_puppet' def get_uids(self): - return list(self.role_resolver.resolve(consts.TASK_ROLES.all)) + return list(self.resolver.resolve(consts.TASK_ROLES.all)) def serialize(self): src_path = self.task['parameters']['src'].format( @@ -245,7 +246,7 @@ class IronicCopyBootstrapKey(CopyKeys): identity = 'ironic_copy_bootstrap_key' def should_execute(self): - return len(self.role_resolver.resolve(['ironic'])) > 0 + return len(self.resolver.resolve(['ironic'])) > 0 class RestartRadosGW(GenericRolesHook): @@ -265,9 +266,9 @@ class CreateVMsOnCompute(GenericRolesHook): identity = 'generate_vms' hook_type = 'puppet' - def __init__(self, task, cluster, nodes, role_resolver=None): + def __init__(self, task, cluster, nodes, resolver=None): super(CreateVMsOnCompute, self).__init__( - task, cluster, [], role_resolver + task, cluster, [], resolver ) self.vm_nodes = objects.Cluster.get_nodes_to_spawn_vms(self.cluster) @@ -345,9 +346,9 @@ class UploadConfiguration(GenericRolesHook): identity = 'upload_configuration' - def __init__(self, task, cluster, nodes, configs=None, role_resolver=None): + def __init__(self, task, cluster, nodes, configs=None, resolver=None): super(UploadConfiguration, self).__init__( - task, cluster, nodes, role_resolver=role_resolver + task, cluster, nodes, resolver=resolver ) self.configs = configs diff --git a/nailgun/nailgun/plugins/manager.py b/nailgun/nailgun/plugins/manager.py index 39cf150275..ad8b908706 100644 --- a/nailgun/nailgun/plugins/manager.py +++ b/nailgun/nailgun/plugins/manager.py @@ -328,9 +328,23 @@ class PluginManager(object): return result + @classmethod + def get_tags_metadata(cls, cluster): + """Get tags metadata for all plugins enabled for the cluster + + :param cluster: A cluster instance + :type cluster: Cluster model + :return: dict -- Object with merged tags data from plugins + """ + tags_metadata = {} + enabled_plugins = ClusterPlugin.get_enabled(cluster.id) + for plugin in enabled_plugins: + tags_metadata.update(plugin.tags_metadata) + return tags_metadata + @classmethod def get_volumes_metadata(cls, cluster): - """Get volumes metadata for cluster from all plugins which enabled it. + """Get volumes metadata for all plugins enabled for the cluster :param cluster: A cluster instance :type cluster: Cluster model diff --git a/nailgun/nailgun/statistics/fuel_statistics/installation_info.py b/nailgun/nailgun/statistics/fuel_statistics/installation_info.py index 18930d6e75..1e8becd9c4 100644 --- a/nailgun/nailgun/statistics/fuel_statistics/installation_info.py +++ b/nailgun/nailgun/statistics/fuel_statistics/installation_info.py @@ -191,6 +191,7 @@ class InstallationInfo(object): WhiteListRule(('attributes_metadata',), 'attributes_metadata', None), WhiteListRule(('volumes_metadata',), 'volumes_metadata', None), WhiteListRule(('roles_metadata',), 'roles_metadata', None), + WhiteListRule(('tags_metadata',), 'tags_metadata', None), WhiteListRule(('network_roles_metadata',), 'network_roles_metadata', None), WhiteListRule(('components_metadata',), 'components_metadata', None), @@ -213,7 +214,7 @@ class InstallationInfo(object): WhiteListRule(('name',), 'name', None), WhiteListRule(('labels',), 'labels', None), WhiteListRule(('roles',), 'roles', None), - WhiteListRule(('primary_roles',), 'primary_roles', None), + WhiteListRule(('primary_tags',), 'primary_tags', None), WhiteListRule(('os_platform',), 'os', None), WhiteListRule(('manufacturer',), 'manufacturer', None), WhiteListRule(('platform_name',), 'platform_name', None), @@ -310,6 +311,7 @@ class InstallationInfo(object): 'components': cluster.components, 'cluster_plugins': cluster.cluster_plugins, 'roles_metadata': cluster.roles_metadata, + 'tags_metadata': cluster.tags_metadata, 'volumes_metadata': cluster.volumes_metadata, } clusters_info.append(cluster_info) diff --git a/nailgun/nailgun/task/legacy_tasks_adapter.py b/nailgun/nailgun/task/legacy_tasks_adapter.py index 8024a1af3d..88a1876c55 100644 --- a/nailgun/nailgun/task/legacy_tasks_adapter.py +++ b/nailgun/nailgun/task/legacy_tasks_adapter.py @@ -93,12 +93,12 @@ def _add_cross_depends(task, depends): return task -def adapt_legacy_tasks(deployment_tasks, legacy_plugin_tasks, role_resolver): +def adapt_legacy_tasks(deployment_tasks, legacy_plugin_tasks, resolver): """Adapt the legacy tasks to execute with Task Based Engine. :param deployment_tasks: the list of deployment tasks :param legacy_plugin_tasks: the pre/post tasks from tasks.yaml - :param role_resolver: the RoleResolver instance + :param resolver: the TagResolver instance """ min_task_version = StrictVersion(consts.TASK_CROSS_DEPENDENCY) @@ -134,7 +134,7 @@ def adapt_legacy_tasks(deployment_tasks, legacy_plugin_tasks, role_resolver): elif task['id'] in post_deployment_graph.node: required_for.add(TASK_END_TEMPLATE.format('post_deployment')) else: - for role in role_resolver.get_all_roles(_get_role(task)): + for role in resolver.get_all_roles(_get_role(task)): required_for.add(TASK_END_TEMPLATE.format(role)) task['required_for'] = list(required_for) if task_version < min_task_version: @@ -167,7 +167,7 @@ def adapt_legacy_tasks(deployment_tasks, legacy_plugin_tasks, role_resolver): logger.info("Added cross_depends for legacy task: %s", task['id']) task_depends = [ {'name': TASK_START_TEMPLATE.format(g), 'role': 'self'} - for g in role_resolver.get_all_roles(_get_role(task)) + for g in resolver.get_all_roles(_get_role(task)) ] yield _add_cross_depends(task, task_depends) diff --git a/nailgun/nailgun/task/task.py b/nailgun/nailgun/task/task.py index 5f2afb0573..d722d9be0e 100644 --- a/nailgun/nailgun/task/task.py +++ b/nailgun/nailgun/task/task.py @@ -56,8 +56,8 @@ from nailgun.task.fake import FAKE_THREADS from nailgun.task.helpers import TaskHelper from nailgun.task.legacy_tasks_adapter import adapt_legacy_tasks from nailgun.utils import logs as logs_utils +from nailgun.utils.resolvers import TagResolver from nailgun.utils.restrictions import VmwareAttributesRestriction -from nailgun.utils.role_resolver import RoleResolver def make_astute_message(task, method, respond_to, args): @@ -337,7 +337,7 @@ class DeploymentTask(BaseDeploymentTask): # NOTE(dshulyak) At this point parts of the orchestration can be empty, # it should not cause any issues with deployment/progress and was # done by design - role_resolver = RoleResolver(nodes) + resolver = TagResolver(nodes) serialized_cluster = deployment_serializers.serialize( graph, transaction.cluster, nodes) @@ -346,10 +346,10 @@ class DeploymentTask(BaseDeploymentTask): pre_deployment = stages.pre_deployment_serialize( graph, transaction.cluster, nodes, - role_resolver=role_resolver) + resolver=resolver) post_deployment = stages.post_deployment_serialize( graph, transaction.cluster, nodes, - role_resolver=role_resolver) + resolver=resolver) if affected_nodes: graph.reexecutable_tasks(events) @@ -362,10 +362,10 @@ class DeploymentTask(BaseDeploymentTask): pre_deployment_affected = stages.pre_deployment_serialize( graph, transaction.cluster, affected_nodes, - role_resolver=role_resolver) + resolver=resolver) post_deployment_affected = stages.post_deployment_serialize( graph, transaction.cluster, affected_nodes, - role_resolver=role_resolver) + resolver=resolver) cls._extend_tasks_list(pre_deployment, pre_deployment_affected) cls._extend_tasks_list(post_deployment, post_deployment_affected) @@ -555,7 +555,7 @@ class ClusterTransaction(DeploymentTask): # TODO(bgaifullin) Primary roles applied in deployment_serializers # need to move this code from deployment serializer # also role resolver should be created after serialization completed - role_resolver = RoleResolver(nodes) + resolver = TagResolver(nodes) cluster = transaction.cluster if objects.Cluster.is_propagate_task_deploy_enabled(cluster): @@ -566,12 +566,12 @@ class ClusterTransaction(DeploymentTask): ) else: plugin_tasks = None - tasks = adapt_legacy_tasks(tasks, plugin_tasks, role_resolver) + tasks = adapt_legacy_tasks(tasks, plugin_tasks, resolver) directory, graph, metadata = lcm.TransactionSerializer.serialize( context, tasks, - role_resolver, + resolver, ) logger.info("tasks serialization is finished.") diff --git a/nailgun/nailgun/test/integration/test_orchestrator_serializer.py b/nailgun/nailgun/test/integration/test_orchestrator_serializer.py index 3700a96793..e16eebfbc6 100644 --- a/nailgun/nailgun/test/integration/test_orchestrator_serializer.py +++ b/nailgun/nailgun/test/integration/test_orchestrator_serializer.py @@ -155,7 +155,7 @@ class TestReplacedDeploymentInfoSerialization(OrchestratorSerializerTestBase): self.cluster = self.env.create( release_kwargs={'version': self.env_version}, cluster_kwargs={'api': False}) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) def test_replaced_tasks_is_not_preserved(self): node = self.env.create_node( @@ -184,7 +184,7 @@ class TestNovaOrchestratorSerializer(OrchestratorSerializerTestBase): def setUp(self): super(TestNovaOrchestratorSerializer, self).setUp() self.cluster = self.create_env(consts.CLUSTER_MODES.ha_compact) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) def create_env(self, mode, network_manager='FlatDHCPManager'): node_args = [ @@ -488,7 +488,7 @@ class TestNovaNetworkOrchestratorSerializer61(OrchestratorSerializerTestBase): cluster_db = self.db.query(Cluster).get(cluster['id']) objects.Cluster.prepare_for_deployment(cluster_db) - objects.Cluster.set_primary_roles(cluster_db, cluster_db.nodes) + objects.Cluster.set_primary_tags(cluster_db, cluster_db.nodes) self.db.flush() return cluster_db @@ -743,7 +743,7 @@ class TestNeutronOrchestratorSerializer61(OrchestratorSerializerTestBase): cluster_db = self.db.query(Cluster).get(cluster['id']) objects.Cluster.prepare_for_deployment(cluster_db) - objects.Cluster.set_primary_roles(cluster_db, cluster_db.nodes) + objects.Cluster.set_primary_tags(cluster_db, cluster_db.nodes) self.db.flush() return cluster_db @@ -1227,7 +1227,7 @@ class TestNovaOrchestratorHASerializer(OrchestratorSerializerTestBase): def setUp(self): super(TestNovaOrchestratorHASerializer, self).setUp() self.cluster = self.create_env(consts.CLUSTER_MODES.ha_compact) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) def create_env(self, mode): cluster = self.env.create( @@ -1454,7 +1454,7 @@ class TestNeutronOrchestratorSerializer(OrchestratorSerializerTestBase): def setUp(self): super(TestNeutronOrchestratorSerializer, self).setUp() self.cluster = self.create_env(consts.CLUSTER_MODES.ha_compact) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) def create_env(self, mode, segment_type='vlan'): release_kwargs = {} @@ -2128,10 +2128,11 @@ class TestNeutronOrchestratorHASerializer(OrchestratorSerializerTestBase): def setUp(self): super(TestNeutronOrchestratorHASerializer, self).setUp() self.cluster = self.create_env(consts.CLUSTER_MODES.ha_compact) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) def create_env(self, mode): cluster = self.env.create( + api=True, release_kwargs={'version': self.env_version}, cluster_kwargs={ 'mode': mode, @@ -2857,7 +2858,7 @@ class TestDeploymentGraphlessSerializers(OrchestratorSerializerTestBase): {'roles': [], 'pending_roles': ['cinder'], 'pending_addition': True}] ) - objects.Cluster.set_primary_roles(self.cluster, self.cluster.nodes) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) @property def serializer(self): diff --git a/nailgun/nailgun/test/integration/test_orchestrator_serializer_70.py b/nailgun/nailgun/test/integration/test_orchestrator_serializer_70.py index c346fa9887..9764dfd673 100644 --- a/nailgun/nailgun/test/integration/test_orchestrator_serializer_70.py +++ b/nailgun/nailgun/test/integration/test_orchestrator_serializer_70.py @@ -766,11 +766,12 @@ class TestPluginDeploymentTasksInjection70(base.BaseIntegrationTest): 'net_segment_type': consts.NEUTRON_SEGMENT_TYPES.vlan, }, nodes_kwargs=[ - {'roles': ['controller'], 'primary_roles': ['controller'], + {'roles': ['controller'], 'primary_tags': ['controller'], 'pending_addition': True} ] ) + objects.Cluster.set_primary_tags(self.cluster, self.cluster.nodes) self.plugin_data = { 'package_version': '3.0.0', 'releases': [ @@ -1128,8 +1129,10 @@ class TestRolesSerializationWithPlugins(BaseDeploymentSerializer, objects.Cluster.prepare_for_deployment(self.cluster) serializer = self._get_serializer(self.cluster) - serialized_data = serializer.serialize( - self.cluster, self.cluster.nodes) + with mock.patch('nailgun.objects.node.Node.all_tags', + mock.Mock(return_value=['test_role'])): + serialized_data = serializer.serialize( + self.cluster, self.cluster.nodes) serialized_data = deployment_info_to_legacy(serialized_data) self.assertItemsEqual(serialized_data[0]['tasks'], [{ 'parameters': { @@ -1868,7 +1871,7 @@ class TestNetworkTemplateSerializer70(BaseDeploymentSerializer, def check_vendor_specific_is_not_set(self, use_net_template=False): node = self.env.create_node( cluster_id=self.cluster.id, - roles=['controller'], primary_roles=['controller'] + roles=['controller'], primary_tags=['controller'] ) objects.Cluster.set_network_template( self.cluster, diff --git a/nailgun/nailgun/test/integration/test_orchestrator_serializer_80.py b/nailgun/nailgun/test/integration/test_orchestrator_serializer_80.py index b6ab0a6b94..70e95822cd 100644 --- a/nailgun/nailgun/test/integration/test_orchestrator_serializer_80.py +++ b/nailgun/nailgun/test/integration/test_orchestrator_serializer_80.py @@ -316,7 +316,7 @@ class TestDeploymentAttributesSerialization80( def test_neutron_attrs(self): self.env.create_node( cluster_id=self.cluster_db.id, - roles=['controller'], primary_roles=['controller'] + roles=['controller'], primary_tags=['controller'] ) objects.Cluster.prepare_for_deployment(self.cluster_db) serialized_for_astute = self.serializer.serialize( @@ -340,7 +340,7 @@ class TestDeploymentAttributesSerialization80( def test_baremetal_transformations(self): self.env._set_additional_component(self.cluster_db, 'ironic', True) self.env.create_node(cluster_id=self.cluster_db.id, - roles=['primary-controller']) + roles=['controller']) objects.Cluster.prepare_for_deployment(self.cluster_db) serialized_for_astute = self.serializer.serialize( self.cluster_db, self.cluster_db.nodes) diff --git a/nailgun/nailgun/test/integration/test_orchestrator_serializer_90.py b/nailgun/nailgun/test/integration/test_orchestrator_serializer_90.py index 43df4b6e98..403379c93f 100644 --- a/nailgun/nailgun/test/integration/test_orchestrator_serializer_90.py +++ b/nailgun/nailgun/test/integration/test_orchestrator_serializer_90.py @@ -802,7 +802,7 @@ class TestNetworkTemplateSerializer90CompatibleWith80( def check_vendor_specific_is_not_set(self, use_net_template=False): node = self.env.create_node( cluster_id=self.cluster.id, - roles=['controller'], primary_roles=['controller'] + roles=['controller'], primary_tags=['controller'] ) objects.Cluster.set_network_template( self.cluster, @@ -863,7 +863,7 @@ class TestNetworkTemplateSerializer90( def check_selective_gateway(self, use_net_template=False): node = self.env.create_node( cluster_id=self.cluster.id, - roles=['controller'], primary_roles=['controller'] + roles=['controller'], primary_tags=['controller'] ) objects.Cluster.set_network_template( self.cluster, diff --git a/nailgun/nailgun/test/integration/test_task_managers.py b/nailgun/nailgun/test/integration/test_task_managers.py index 32e0a73029..bdd5b21f1b 100644 --- a/nailgun/nailgun/test/integration/test_task_managers.py +++ b/nailgun/nailgun/test/integration/test_task_managers.py @@ -193,7 +193,7 @@ class TestTaskManagers(BaseIntegrationTest): @mock.patch('nailgun.task.task.rpc.cast') @mock.patch('objects.Cluster.get_deployment_tasks') - def test_deployment_tasks_assigned_for_primary_roles( + def test_deployment_tasks_assigned_for_primary_tags( self, tasks_mock, rpc_mock ): tasks_mock.return_value = [ @@ -1187,16 +1187,16 @@ class TestTaskManagers(BaseIntegrationTest): nodes_kwargs=[{'roles': ['controller'], 'status': consts.NODE_STATUSES.ready}] * 3) task_manager = manager.NodeDeletionTaskManager(cluster_id=cluster.id) - objects.Cluster.set_primary_roles(cluster, self.env.nodes) + objects.Cluster.set_primary_tags(cluster, self.env.nodes) primary_node = filter( - lambda n: 'controller' in n.primary_roles, + lambda n: 'controller' in n.primary_tags, self.env.nodes)[0] task_manager.execute([primary_node]) self.env.refresh_nodes() new_primary = filter( - lambda n: ('controller' in n.primary_roles and + lambda n: ('primary-controller' in objects.Node.all_tags(n) and n.pending_deletion is False), self.env.nodes)[0] diff --git a/nailgun/nailgun/test/integration/test_transactions_manager.py b/nailgun/nailgun/test/integration/test_transactions_manager.py index 16d45ab14c..13042c81c8 100644 --- a/nailgun/nailgun/test/integration/test_transactions_manager.py +++ b/nailgun/nailgun/test/integration/test_transactions_manager.py @@ -578,7 +578,7 @@ class TestTransactionManager(base.BaseIntegrationTest): self.assertEqual(task.status, consts.TASK_STATUSES.ready) @mock.patch('nailgun.transactions.manager.rpc') - def test_execute_for_primary_roles(self, rpc_mock): + def test_execute_for_primary_tags(self, rpc_mock): self.graph.tasks.append(objects.DeploymentGraphTask.create( { 'id': 'test_task_2', diff --git a/nailgun/nailgun/test/unit/test_downgrade_fuel_11_0.py b/nailgun/nailgun/test/unit/test_downgrade_fuel_11_0.py new file mode 100644 index 0000000000..60389450e7 --- /dev/null +++ b/nailgun/nailgun/test/unit/test_downgrade_fuel_11_0.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +# Copyright 2016 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import datetime + +import alembic +from oslo_serialization import jsonutils + +import sqlalchemy as sa + +from nailgun.db import db +from nailgun.db import dropdb +from nailgun.db.migration import ALEMBIC_CONFIG +from nailgun.test import base + + +_prepare_revision = 'dc8bc8751c42' +_test_revision = 'c6edea552f1e' + + +def setup_module(): + dropdb() + alembic.command.upgrade(ALEMBIC_CONFIG, _prepare_revision) + prepare() + alembic.command.downgrade(ALEMBIC_CONFIG, _test_revision) + + +def prepare(): + meta = base.reflect_db_metadata() + + result = db.execute( + meta.tables['releases'].insert(), + [{ + 'name': 'test_name', + 'version': '2016.1-11.0', + 'operating_system': 'ubuntu', + 'state': 'available', + 'roles': jsonutils.dumps([ + 'controller', + ]), + 'roles_metadata': jsonutils.dumps({ + 'controller': { + 'name': 'Controller', + }, + }), + 'is_deployable': True + }]) + + release_id = result.inserted_primary_key[0] + + result = db.execute( + meta.tables['clusters'].insert(), + [{ + 'name': 'test_env1', + 'release_id': release_id, + 'mode': 'ha_compact', + 'status': 'operational', + 'net_provider': 'neutron', + 'grouping': 'roles', + 'fuel_version': '10.0', + }]) + cluster_id = result.inserted_primary_key[0] + + result = db.execute( + meta.tables['nodes'].insert(), + [{ + 'uuid': 'fcd49872-3917-4a18-98f9-3f5acfe3fdec', + 'cluster_id': cluster_id, + 'group_id': None, + 'status': 'ready', + 'roles': ['role_x', 'role_y'], + 'primary_tags': ['role_y', 'test'], + 'meta': '{}', + 'mac': 'bb:aa:aa:aa:aa:aa', + 'timestamp': datetime.datetime.utcnow(), + }] + ) + + db.commit() + + +class TestPluginTags(base.BaseAlembicMigrationTest): + def test_primary_tags_downgrade(self): + nodes = self.meta.tables['nodes'] + query = sa.select([nodes.c.primary_roles]).where( + nodes.c.uuid == 'fcd49872-3917-4a18-98f9-3f5acfe3fdec') + primary_roles = db.execute(query).fetchone()[0] + self.assertItemsEqual(primary_roles, ['role_y']) diff --git a/nailgun/nailgun/test/unit/test_lcm_transaction_serializer.py b/nailgun/nailgun/test/unit/test_lcm_transaction_serializer.py index c64d90f769..6450e19e0d 100644 --- a/nailgun/nailgun/test/unit/test_lcm_transaction_serializer.py +++ b/nailgun/nailgun/test/unit/test_lcm_transaction_serializer.py @@ -20,7 +20,7 @@ import multiprocessing.dummy from nailgun import consts from nailgun import errors from nailgun import lcm -from nailgun.utils.role_resolver import RoleResolver +from nailgun.utils.resolvers import TagResolver from nailgun.test.base import BaseUnitTest @@ -108,13 +108,13 @@ class TestTransactionSerializer(BaseUnitTest): } }) - with mock.patch('nailgun.utils.role_resolver.objects') as m_objects: - m_objects.Node.all_roles = lambda x: x.roles - cls.role_resolver = RoleResolver(cls.nodes) + with mock.patch('nailgun.utils.resolvers.objects') as m_objects: + m_objects.Node.all_tags = lambda x: x.roles + cls.resolver = TagResolver(cls.nodes) def test_serialize_integration(self): serialized = lcm.TransactionSerializer.serialize( - self.context, self.tasks, self.role_resolver + self.context, self.tasks, self.resolver )[1] # controller self.datadiff( @@ -191,7 +191,7 @@ class TestTransactionSerializer(BaseUnitTest): def test_resolve_nodes(self): serializer = lcm.TransactionSerializer( - self.context, self.role_resolver + self.context, self.resolver ) self.assertEqual( [None], @@ -219,7 +219,7 @@ class TestTransactionSerializer(BaseUnitTest): def test_dependencies_de_duplication(self): serializer = lcm.TransactionSerializer( - self.context, self.role_resolver + self.context, self.resolver ) serializer.tasks_graph = { None: {}, @@ -277,7 +277,7 @@ class TestTransactionSerializer(BaseUnitTest): 'tasks': ['task4', 'task2'] }) serialized = lcm.TransactionSerializer.serialize( - self.context, tasks, self.role_resolver + self.context, tasks, self.resolver ) tasks_per_node = serialized[1] self.datadiff( @@ -326,7 +326,7 @@ class TestTransactionSerializer(BaseUnitTest): def test_expand_dependencies(self): serializer = lcm.TransactionSerializer( - self.context, self.role_resolver + self.context, self.resolver ) serializer.tasks_graph = { '1': {'task1': {}}, @@ -342,7 +342,7 @@ class TestTransactionSerializer(BaseUnitTest): def test_expand_cross_dependencies(self): serializer = lcm.TransactionSerializer( - self.context, self.role_resolver + self.context, self.resolver ) serializer.tasks_graph = { '1': {'task1': {}, 'task2': {}}, @@ -380,7 +380,7 @@ class TestTransactionSerializer(BaseUnitTest): def test_need_update_task(self): serializer = lcm.TransactionSerializer( - self.context, self.role_resolver + self.context, self.resolver ) self.assertTrue(serializer.need_update_task( {}, {"id": "task1", "type": "puppet"} diff --git a/nailgun/nailgun/test/unit/test_legacy_tasks_adapter.py b/nailgun/nailgun/test/unit/test_legacy_tasks_adapter.py index 8d67744ebe..289b749326 100644 --- a/nailgun/nailgun/test/unit/test_legacy_tasks_adapter.py +++ b/nailgun/nailgun/test/unit/test_legacy_tasks_adapter.py @@ -59,9 +59,9 @@ class TestLegacyTasksAdapter(BaseTestCase): @classmethod def setUpClass(cls): super(TestLegacyTasksAdapter, cls).setUpClass() - cls.role_resolver = mock.MagicMock() - cls.role_resolver.get_all_roles.side_effect = \ - cls.role_resolver_side_effect + cls.resolver = mock.MagicMock() + cls.resolver.get_all_roles.side_effect = \ + cls.resolver_side_effect def test_returns_same_task_if_no_legacy(self): tasks = [ @@ -71,7 +71,7 @@ class TestLegacyTasksAdapter(BaseTestCase): {'id': 'group1', 'type': consts.ORCHESTRATOR_TASK_TYPES.group}, {'id': 'stage1', 'type': consts.ORCHESTRATOR_TASK_TYPES.stage} ] - new_tasks = list(adapt_legacy_tasks(tasks, None, self.role_resolver)) + new_tasks = list(adapt_legacy_tasks(tasks, None, self.resolver)) self.datadiff(tasks, new_tasks, ignore_keys='required_for') self.assertEqual([], tasks[0].get('required_for', [])) self.assertEqual( @@ -79,7 +79,7 @@ class TestLegacyTasksAdapter(BaseTestCase): ) @staticmethod - def role_resolver_side_effect(roles): + def resolver_side_effect(roles): if isinstance(roles, six.string_types): roles = [roles] return set(roles) @@ -118,7 +118,7 @@ class TestLegacyTasksAdapter(BaseTestCase): ] tasks.extend(stages) - new_tasks = list(adapt_legacy_tasks(tasks, [], self.role_resolver)) + new_tasks = list(adapt_legacy_tasks(tasks, [], self.resolver)) self.assertEqual( { @@ -297,7 +297,7 @@ class TestLegacyTasksAdapter(BaseTestCase): } ] new_tasks = list(adapt_legacy_tasks( - tasks, legacy_plugin_tasks, self.role_resolver + tasks, legacy_plugin_tasks, self.resolver )) stage1_tasks = new_tasks[-5:-2] depends = [{'role': None, 'name': 'stage1_end'}] diff --git a/nailgun/nailgun/test/unit/test_migration_fuel_11_0.py b/nailgun/nailgun/test/unit/test_migration_fuel_11_0.py new file mode 100644 index 0000000000..7f333f9987 --- /dev/null +++ b/nailgun/nailgun/test/unit/test_migration_fuel_11_0.py @@ -0,0 +1,115 @@ +# Copyright 2016 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import datetime + +import alembic +from oslo_serialization import jsonutils +import six +import sqlalchemy as sa + +from nailgun.db import db +from nailgun.db import dropdb +from nailgun.db.migration import ALEMBIC_CONFIG +from nailgun.test import base + +_prepare_revision = 'c6edea552f1e' +_test_revision = 'dc8bc8751c42' + + +def setup_module(): + dropdb() + alembic.command.upgrade(ALEMBIC_CONFIG, _prepare_revision) + prepare() + alembic.command.upgrade(ALEMBIC_CONFIG, _test_revision) + + +def prepare(): + meta = base.reflect_db_metadata() + + result = db.execute( + meta.tables['releases'].insert(), + [{ + 'name': 'test_name', + 'version': '2016.1-11.0', + 'operating_system': 'ubuntu', + 'state': 'available', + 'roles': jsonutils.dumps([ + 'controller', + ]), + 'roles_metadata': jsonutils.dumps({ + 'controller': { + 'name': 'Controller', + 'has_primary': True + }, + 'compute': { + 'name': 'Compute' + }, + }), + 'is_deployable': True + }]) + + release_id = result.inserted_primary_key[0] + + result = db.execute( + meta.tables['clusters'].insert(), + [{ + 'name': 'test_env1', + 'release_id': release_id, + 'mode': 'ha_compact', + 'status': 'operational', + 'net_provider': 'neutron', + 'grouping': 'roles', + 'fuel_version': '10.0', + }]) + cluster_id = result.inserted_primary_key[0] + + result = db.execute( + meta.tables['nodes'].insert(), + [{ + 'uuid': 'fcd49872-3917-4a18-98f9-3f5acfe3fdec', + 'cluster_id': cluster_id, + 'group_id': None, + 'status': 'ready', + 'roles': ['role_x', 'role_y'], + 'primary_roles': ['role_y'], + 'meta': '{}', + 'mac': 'bb:aa:aa:aa:aa:aa', + 'timestamp': datetime.datetime.utcnow(), + }] + ) + + db.commit() + + +class TestTags(base.BaseAlembicMigrationTest): + def test_primary_tags_migration(self): + nodes = self.meta.tables['nodes'] + query = sa.select([nodes.c.primary_tags]).where( + nodes.c.uuid == 'fcd49872-3917-4a18-98f9-3f5acfe3fdec') + primary_tags = db.execute(query).fetchone()[0] + self.assertItemsEqual(primary_tags, ['role_y']) + + def test_tags_meta_migration(self): + releases = self.meta.tables['releases'] + query = sa.select([releases.c.roles_metadata, + releases.c.tags_metadata]) + for roles_meta, tags_meta in db.execute(query): + tags_meta = jsonutils.loads(tags_meta) + for role_name, role_meta in six.iteritems( + jsonutils.loads(roles_meta)): + self.assertEqual( + tags_meta[role_name].get('has_primary', False), + role_meta.get('has_primary', False) + ) diff --git a/nailgun/nailgun/test/unit/test_objects.py b/nailgun/nailgun/test/unit/test_objects.py index 0ca23caec7..feb246410c 100644 --- a/nailgun/nailgun/test/unit/test_objects.py +++ b/nailgun/nailgun/test/unit/test_objects.py @@ -209,7 +209,7 @@ class TestNodeObject(BaseIntegrationTest): self.assertEqual(new_cluster.id, node.cluster_id) self.assertEqual(new_group.id, node.group_id) self.assertEqual([], node.roles) - self.assertEqual([], node.primary_roles) + self.assertEqual([], node.primary_tags) self.assertItemsEqual(roles, node.pending_roles) self.assertEqual(node.attributes, cluster.release.node_attributes) @@ -257,7 +257,7 @@ class TestNodeObject(BaseIntegrationTest): self.assertEqual(new_cluster.id, node.cluster_id) self.assertEqual(new_group.id, node.group_id) self.assertEqual([], node.roles) - self.assertEqual([], node.primary_roles) + self.assertEqual([], node.primary_tags) self.assertItemsEqual(roles, node.pending_roles) self.assertIsNotNone(node.network_template) endpoints = node.network_template['templates']['common']['endpoints'] diff --git a/nailgun/nailgun/test/unit/test_plugins_serializers.py b/nailgun/nailgun/test/unit/test_plugins_serializers.py index f7e43059d1..e3566064f0 100644 --- a/nailgun/nailgun/test/unit/test_plugins_serializers.py +++ b/nailgun/nailgun/test/unit/test_plugins_serializers.py @@ -26,7 +26,7 @@ from nailgun.orchestrator.plugins_serializers import \ from nailgun.orchestrator.plugins_serializers import \ PluginsPreDeploymentHooksSerializer from nailgun.test import base -from nailgun.utils.role_resolver import NullResolver +from nailgun.utils.resolvers import NullResolver class TestBasePluginDeploymentHooksSerializer(base.BaseTestCase): @@ -42,7 +42,7 @@ class TestBasePluginDeploymentHooksSerializer(base.BaseTestCase): self.hook = BasePluginDeploymentHooksSerializer( self.nodes, self.cluster, - role_resolver=NullResolver({x['id'] for x in self.nodes}) + resolver=NullResolver([x['id'] for x in self.nodes]) ) def test_tasks_are_serializable(self): @@ -161,7 +161,7 @@ class TestTasksDeploymentOrder(base.BaseTestCase): self.hook = BasePluginDeploymentHooksSerializer( self.nodes, self.cluster, - role_resolver=NullResolver([x['id'] for x in self.nodes]) + resolver=NullResolver([x['id'] for x in self.nodes]) ) def make_plugin_mock_with_stages(self, plugin_name, stages): @@ -238,7 +238,7 @@ class TestPluginsPreDeploymentHooksSerializer( self.hook = PluginsPreDeploymentHooksSerializer( self.cluster, self.nodes, - role_resolver=NullResolver([x['id'] for x in self.nodes])) + resolver=NullResolver([x['id'] for x in self.nodes])) @mock.patch( 'nailgun.orchestrator.plugins_serializers.' @@ -298,7 +298,7 @@ class TestPluginsPostDeploymentHooksSerializer( self.hook = PluginsPostDeploymentHooksSerializer( self.cluster, self.nodes, - role_resolver=NullResolver([x['id'] for x in self.nodes])) + resolver=NullResolver([x['id'] for x in self.nodes])) def test_serialize_begin_tasks(self): self.assertItemsEqual(self.hook.serialize_begin_tasks(), list()) diff --git a/nailgun/nailgun/test/unit/test_primary_roles_assignment.py b/nailgun/nailgun/test/unit/test_primary_tags_assignment.py similarity index 83% rename from nailgun/nailgun/test/unit/test_primary_roles_assignment.py rename to nailgun/nailgun/test/unit/test_primary_tags_assignment.py index 11b94f241a..cd01c2e89e 100644 --- a/nailgun/nailgun/test/unit/test_primary_roles_assignment.py +++ b/nailgun/nailgun/test/unit/test_primary_tags_assignment.py @@ -18,6 +18,7 @@ from contextlib import contextmanager import six from nailgun import consts +from nailgun.db import db from nailgun import objects from nailgun.test import base @@ -49,13 +50,13 @@ class BasePrimaryRolesAssignmentTestCase(base.BaseTestCase): {'pending_roles': [self.role_name], 'status': consts.NODE_STATUSES.discover, 'pending_addition': True}]) - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) nodes = sorted(cluster.nodes, key=lambda node: node.id) # with lowest uid is assigned as primary self.assertEqual( - objects.Node.all_roles(nodes[0]), [self.primary_role_name]) + objects.Node.all_tags(nodes[0]), [self.primary_role_name]) self.assertEqual( - objects.Node.all_roles(nodes[1]), [self.role_name]) + objects.Node.all_tags(nodes[1]), [self.role_name]) def test_primary_controller_assigned_for_ready_node(self): cluster = self.env.create( @@ -69,16 +70,16 @@ class BasePrimaryRolesAssignmentTestCase(base.BaseTestCase): {'roles': [self.role_name], 'status': consts.NODE_STATUSES.ready, 'pending_addition': True}]) - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) # primary assigned to node with ready status nodes = sorted(cluster.nodes, key=lambda node: node.id) ready_node = next(n for n in cluster.nodes if n.status == consts.NODE_STATUSES.ready) self.assertEqual(nodes[1], ready_node) self.assertEqual( - objects.Node.all_roles(nodes[1]), [self.primary_role_name]) + objects.Node.all_tags(nodes[1]), [self.primary_role_name]) self.assertEqual( - objects.Node.all_roles(nodes[0]), [self.role_name]) + objects.Node.all_tags(nodes[0]), [self.role_name]) def test_primary_assignment_multinode(self): """Primary should not be assigned in multinode env.""" @@ -95,11 +96,11 @@ class BasePrimaryRolesAssignmentTestCase(base.BaseTestCase): {'roles': [self.role_name], 'status': consts.NODE_STATUSES.ready, 'pending_addition': True}]) - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) self.assertEqual( - objects.Node.all_roles(cluster.nodes[0]), [self.role_name]) + objects.Node.all_tags(cluster.nodes[0]), [self.role_name]) self.assertEqual( - objects.Node.all_roles(cluster.nodes[1]), [self.role_name]) + objects.Node.all_tags(cluster.nodes[1]), [self.role_name]) def test_primary_not_assigned_to_pending_deletion(self): cluster = self.env.create( @@ -110,9 +111,9 @@ class BasePrimaryRolesAssignmentTestCase(base.BaseTestCase): {'roles': [self.role_name], 'status': consts.NODE_STATUSES.ready, 'pending_deletion': True}]) - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) self.assertEqual( - objects.Node.all_roles(cluster.nodes[0]), [self.role_name]) + objects.Node.all_tags(cluster.nodes[0]), [self.role_name]) @contextmanager def assert_node_reassigned(self): @@ -127,16 +128,17 @@ class BasePrimaryRolesAssignmentTestCase(base.BaseTestCase): {'roles': [self.role_name], 'status': consts.NODE_STATUSES.ready, 'pending_addition': True}]) - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) nodes = sorted(cluster.nodes, key=lambda node: node.id) self.assertEqual( - objects.Node.all_roles(nodes[1]), [self.primary_role_name]) + objects.Node.all_tags(nodes[1]), [self.primary_role_name]) self.assertEqual( - objects.Node.all_roles(nodes[0]), [self.role_name]) + objects.Node.all_tags(nodes[0]), [self.role_name]) yield nodes[1] - objects.Cluster.set_primary_roles(cluster, cluster.nodes) + db().refresh(cluster) + objects.Cluster.set_primary_tags(cluster, cluster.nodes) self.assertEqual( - objects.Node.all_roles(nodes[0]), [self.primary_role_name]) + objects.Node.all_tags(nodes[0]), [self.primary_role_name]) def test_primary_assign_after_reset_to_discovery(self): """Removing primary roles after resetting node to discovery""" diff --git a/nailgun/nailgun/test/unit/test_role_resolver.py b/nailgun/nailgun/test/unit/test_role_resolver.py index 75487ffe06..929f204ebe 100644 --- a/nailgun/nailgun/test/unit/test_role_resolver.py +++ b/nailgun/nailgun/test/unit/test_role_resolver.py @@ -19,10 +19,10 @@ import six from nailgun import consts from nailgun.test.base import BaseUnitTest -from nailgun.utils import role_resolver +from nailgun.utils import resolvers -class TestPatternBasedRoleResolver(BaseUnitTest): +class TestPatternBasedTagResolver(BaseUnitTest): @classmethod def setUpClass(cls): cls.roles_of_nodes = [ @@ -38,12 +38,12 @@ class TestPatternBasedRoleResolver(BaseUnitTest): ] def setUp(self): - objs_mock = mock.patch('nailgun.utils.role_resolver.objects') + objs_mock = mock.patch('nailgun.utils.resolvers.objects') self.addCleanup(objs_mock.stop) - objs_mock.start().Node.all_roles.side_effect = self.roles_of_nodes + objs_mock.start().Node.all_tags.side_effect = self.roles_of_nodes def test_resolve_by_pattern(self): - resolver = role_resolver.RoleResolver(self.nodes) + resolver = resolvers.TagResolver(self.nodes) self.assertItemsEqual( ["0", "2", "3"], resolver.resolve(["/.*controller/"]) @@ -58,14 +58,14 @@ class TestPatternBasedRoleResolver(BaseUnitTest): ) def test_resolve_all(self): - resolver = role_resolver.RoleResolver(self.nodes) + resolver = resolvers.TagResolver(self.nodes) self.assertItemsEqual( (x.uid for x in self.nodes), resolver.resolve("*") ) def test_resolve_master(self): - resolver = role_resolver.RoleResolver(self.nodes) + resolver = resolvers.TagResolver(self.nodes) self.assertItemsEqual( [consts.MASTER_NODE_UID], resolver.resolve(consts.TASK_ROLES.master) @@ -76,7 +76,7 @@ class TestPatternBasedRoleResolver(BaseUnitTest): ) def test_resolve_any(self): - resolver = role_resolver.RoleResolver(self.nodes) + resolver = resolvers.TagResolver(self.nodes) all_nodes = resolver.resolve("*", consts.NODE_RESOLVE_POLICY.all) self.assertItemsEqual( all_nodes, @@ -87,7 +87,7 @@ class TestPatternBasedRoleResolver(BaseUnitTest): self.assertTrue(any_node.issubset(all_nodes)) def test_get_all_roles(self): - resolver = role_resolver.RoleResolver(self.nodes) + resolver = resolvers.TagResolver(self.nodes) all_roles = {r for roles in self.roles_of_nodes for r in roles} self.assertEqual(all_roles, resolver.get_all_roles()) self.assertEqual(all_roles, resolver.get_all_roles( @@ -108,5 +108,5 @@ class TestNullResolver(BaseUnitTest): node_ids = ['1', '2', '3'] self.assertIs( node_ids, - role_resolver.NullResolver(node_ids).resolve("controller") + resolvers.NullResolver(node_ids).resolve("controller") ) diff --git a/nailgun/nailgun/test/unit/test_stages_task_serialization.py b/nailgun/nailgun/test/unit/test_stages_task_serialization.py index a8e4654567..0b0f0dd487 100644 --- a/nailgun/nailgun/test/unit/test_stages_task_serialization.py +++ b/nailgun/nailgun/test/unit/test_stages_task_serialization.py @@ -45,7 +45,7 @@ class BaseTaskSerializationTest(base.BaseTestCase): self.env.create_node( roles=['controller'], cluster_id=self.cluster.id), self.env.create_node( - roles=['controller'], primary_roles=['controller'], + roles=['controller'], primary_tags=['controller'], cluster_id=self.cluster.id), self.env.create_node( roles=['cinder', 'compute'], cluster_id=self.cluster.id)] @@ -80,7 +80,7 @@ class BaseTaskSerializationTestUbuntu(base.BaseTestCase): self.env.create_node( roles=['controller'], cluster_id=self.cluster.id), self.env.create_node( - roles=['primary-controller'], cluster_id=self.cluster.id), + roles=['controller'], cluster_id=self.cluster.id), self.env.create_node( roles=['cinder', 'compute'], cluster_id=self.cluster.id)] self.all_uids = [n.uid for n in self.nodes] @@ -187,7 +187,7 @@ class TestHooksSerializers(BaseTaskSerializationTest): self.assertFalse(task.should_execute()) @mock.patch.object(NetworkDeploymentSerializer, 'update_nodes_net_info') - @mock.patch.object(objects.Node, 'all_roles') + @mock.patch.object(objects.Node, 'all_tags') def test_upload_nodes_info(self, m_roles, m_update_nodes): # mark one node as ready so we can test for duplicates self.env.nodes[0].status = consts.NODE_STATUSES.ready diff --git a/nailgun/nailgun/test/unit/test_task_based_deployment.py b/nailgun/nailgun/test/unit/test_task_based_deployment.py index 9bf549b7b7..ba75205c30 100644 --- a/nailgun/nailgun/test/unit/test_task_based_deployment.py +++ b/nailgun/nailgun/test/unit/test_task_based_deployment.py @@ -360,7 +360,7 @@ class TestTaskSerializers(BaseTestCase): def test_expand_task_groups(self): node_ids = ['1', '2'] - with mock.patch.object(self.serializer, 'role_resolver') as m_resolve: + with mock.patch.object(self.serializer, 'resolver') as m_resolve: m_resolve.resolve.return_value = node_ids self.serializer.expand_task_groups( [ @@ -433,7 +433,7 @@ class TestTaskSerializers(BaseTestCase): } } - with mock.patch.object(self.serializer, 'role_resolver') as m_resolve: + with mock.patch.object(self.serializer, 'resolver') as m_resolve: m_resolve.resolve.return_value = node_ids # the default role and policy self.assertItemsEqual( @@ -536,7 +536,7 @@ class TestTaskSerializers(BaseTestCase): self.serializer.task_processor.origin_task_ids = { 'task_1': 'task' } - self.serializer.role_resolver = task_based_deployment.NullResolver( + self.serializer.resolver = task_based_deployment.NullResolver( node_ids ) self.serializer.resolve_dependencies() @@ -592,11 +592,11 @@ class TestTaskSerializers(BaseTestCase): def test_deploy_only_selected_nodes(self): tasks = [ { - "id": "test1", "role": ["controller"], + "id": "test1", "role": ["controller"], "tags": ["controller"], "type": "puppet", "version": "2.0.0", "parameters": {} }, { - "id": "test2", "role": ["compute"], + "id": "test2", "role": ["compute"], "tags": ["compute"], "type": "puppet", "version": "2.0.0", "parameters": {} } ] @@ -619,20 +619,20 @@ class TestTaskSerializers(BaseTestCase): def test_serialise_with_events(self): tasks = [ { - "id": "test1", "role": ["controller"], + "id": "test1", "role": ["controller"], "tags": ["controller"], "type": "puppet", "version": "2.0.0", "parameters": {} }, { - "id": "test2", "role": ["compute"], + "id": "test2", "role": ["compute"], "tags": ["compute"], "type": "puppet", "version": "2.0.0", "parameters": {}, "reexecute_on": ["deploy"] }, { - "id": "test3", "role": ["compute"], + "id": "test3", "role": ["compute"], "tags": ["compute"], "type": "puppet", "version": "2.0.0", "parameters": {} }, { - "id": "test4", "role": ["cinder"], + "id": "test4", "role": ["cinder"], "tags": ["cinder"], "type": "puppet", "version": "2.0.0", "parameters": {} } ] diff --git a/nailgun/nailgun/transactions/manager.py b/nailgun/nailgun/transactions/manager.py index 067cef1314..017ed2e754 100644 --- a/nailgun/nailgun/transactions/manager.py +++ b/nailgun/nailgun/transactions/manager.py @@ -34,7 +34,7 @@ from nailgun.task import legacy_tasks_adapter from nailgun.utils import dict_update from nailgun.utils import get_in from nailgun.utils import mule -from nailgun.utils import role_resolver +from nailgun.utils import resolvers from nailgun import yaql_ext @@ -359,8 +359,8 @@ class TransactionsManager(object): # we should initialize primary roles for cluster before # role resolve has been created - objects.Cluster.set_primary_roles(cluster, nodes) - resolver = role_resolver.RoleResolver(nodes) + objects.Cluster.set_primary_tags(cluster, nodes) + resolver = resolvers.TagResolver(nodes) _adjust_graph_tasks( graph, cluster, diff --git a/nailgun/nailgun/utils/role_resolver.py b/nailgun/nailgun/utils/resolvers.py similarity index 98% rename from nailgun/nailgun/utils/role_resolver.py rename to nailgun/nailgun/utils/resolvers.py index ce8cf2150f..176bbd6c80 100644 --- a/nailgun/nailgun/utils/role_resolver.py +++ b/nailgun/nailgun/utils/resolvers.py @@ -67,7 +67,7 @@ class NullResolver(BaseRoleResolver): return [] -class RoleResolver(BaseRoleResolver): +class TagResolver(BaseRoleResolver): """The general role resolver. Allows to use patterns in name of role @@ -86,7 +86,7 @@ class RoleResolver(BaseRoleResolver): """ self.__mapping = defaultdict(set) for node in nodes: - for r in objects.Node.all_roles(node): + for r in objects.Node.all_tags(node): self.__mapping[r].add(node.uid) def resolve(self, roles, policy=None):