Data model changes for Plugin NIC, BOND and Node attributes

This patch:
  * extends current DB model with new entities and provides
    related migrations.
  * extends plugin sync method to support storing new plugins
    attributes.
  * provides cosmetic fix for ClusterPlugin model. Lets write table
    names in the plural but model names in the singular.

Change-Id: I3edbde1d48461ce3fab7c93f17e2db5332b1f7fb
Implements: blueprint nics-and-nodes-attributes-via-plugin
This commit is contained in:
Andriy Popovych 2016-02-26 14:48:58 +02:00
parent c3a868dd32
commit 9d86b078cf
26 changed files with 698 additions and 86 deletions

View File

@ -17,7 +17,7 @@
from nailgun.api.v1.validators.base import BasicValidator
from nailgun.api.v1.validators.json_schema import plugin
from nailgun import errors
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.objects import Plugin
@ -25,7 +25,7 @@ class PluginValidator(BasicValidator):
@classmethod
def validate_delete(cls, data, instance):
if ClusterPlugins.is_plugin_used(instance.id):
if ClusterPlugin.is_plugin_used(instance.id):
raise errors.CannotDelete(
"Can't delete plugin which is enabled "
"for some environment."

View File

@ -23,6 +23,8 @@ Create Date: 2016-04-08 15:20:43.989472
from alembic import op
import sqlalchemy as sa
from nailgun.db.sqlalchemy.models import fields
# revision identifiers, used by Alembic.
revision = 'c6edea552f1e'
@ -31,9 +33,11 @@ down_revision = '675105097a69'
def upgrade():
upgrade_plugin_links_constraints()
upgrade_plugin_with_nics_and_nodes_attributes()
def downgrade():
downgrade_plugin_with_nics_and_nodes_attributes()
downgrade_plugin_links_constraints()
@ -80,3 +84,153 @@ def downgrade_plugin_links_constraints():
'cluster_plugin_links')
op.drop_constraint('plugin_links_url_uc', 'plugin_links')
def upgrade_plugin_with_nics_and_nodes_attributes():
op.add_column(
'plugins',
sa.Column(
'nic_attributes_metadata',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'plugins',
sa.Column(
'bond_attributes_metadata',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'plugins',
sa.Column(
'node_attributes_metadata',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'node_nic_interfaces',
sa.Column(
'attributes',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'node_nic_interfaces',
sa.Column(
'meta',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'node_bond_interfaces',
sa.Column(
'attributes',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'releases',
sa.Column(
'nic_attributes',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.add_column(
'releases',
sa.Column(
'bond_attributes',
fields.JSON(),
nullable=False,
server_default='{}'
)
)
op.create_table(
'node_nic_interface_cluster_plugins',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column(
'attributes', fields.JSON(), nullable=False, server_default='{}'),
sa.Column('cluster_plugin_id', sa.Integer(), nullable=False),
sa.Column('interface_id', sa.Integer(), nullable=False),
sa.Column('node_id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(
['cluster_plugin_id'],
['cluster_plugins.id'],
ondelete='CASCADE'),
sa.ForeignKeyConstraint(
['interface_id'], ['node_nic_interfaces.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(
['node_id'], ['nodes.id'], ondelete='CASCADE')
)
op.create_table(
'node_bond_interface_cluster_plugins',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column(
'attributes', fields.JSON(), nullable=False, server_default='{}'),
sa.Column('cluster_plugin_id', sa.Integer(), nullable=False),
sa.Column('bond_id', sa.Integer(), nullable=False),
sa.Column('node_id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(
['cluster_plugin_id'],
['cluster_plugins.id'],
ondelete='CASCADE'),
sa.ForeignKeyConstraint(
['bond_id'], ['node_bond_interfaces.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(
['node_id'], ['nodes.id'], ondelete='CASCADE')
)
op.create_table(
'node_cluster_plugins',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column(
'attributes', fields.JSON(), nullable=False, server_default='{}'),
sa.Column('cluster_plugin_id', sa.Integer(), nullable=False),
sa.Column('node_id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(
['cluster_plugin_id'],
['cluster_plugins.id'],
ondelete='CASCADE'),
sa.ForeignKeyConstraint(
['node_id'], ['nodes.id'], ondelete='CASCADE')
)
def downgrade_plugin_with_nics_and_nodes_attributes():
op.drop_table('node_cluster_plugins')
op.drop_table('node_bond_interface_cluster_plugins')
op.drop_table('node_nic_interface_cluster_plugins')
op.drop_column('releases', 'bond_attributes')
op.drop_column('releases', 'nic_attributes')
op.drop_column('node_bond_interfaces', 'attributes')
op.drop_column('node_nic_interfaces', 'meta')
op.drop_column('node_nic_interfaces', 'attributes')
op.drop_column('plugins', 'node_attributes_metadata')
op.drop_column('plugins', 'bond_attributes_metadata')
op.drop_column('plugins', 'nic_attributes_metadata')

View File

@ -71,7 +71,12 @@ from nailgun.db.sqlalchemy.models.deployment_history import DeploymentHistory
from nailgun.db.sqlalchemy.models.master_node_settings \
import MasterNodeSettings
from nailgun.db.sqlalchemy.models.plugins import ClusterPlugins
from nailgun.db.sqlalchemy.models.plugins import ClusterPlugin
from nailgun.db.sqlalchemy.models.plugins import NodeClusterPlugin
from nailgun.db.sqlalchemy.models.plugins \
import NodeBondInterfaceClusterPlugin
from nailgun.db.sqlalchemy.models.plugins \
import NodeNICInterfaceClusterPlugin
from nailgun.db.sqlalchemy.models.plugins import Plugin
from nailgun.db.sqlalchemy.models.openstack_config import OpenstackConfig

View File

@ -20,7 +20,7 @@ from sqlalchemy import String
from sqlalchemy import Text
from sqlalchemy import UniqueConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.orm import relationship, backref
from nailgun.db.sqlalchemy.models.base import Base
from nailgun.db.sqlalchemy.models.fields import JSON
@ -28,7 +28,7 @@ from nailgun.db.sqlalchemy.models.mutable import MutableDict
from nailgun.db.sqlalchemy.models.mutable import MutableList
class ClusterPlugins(Base):
class ClusterPlugin(Base):
__tablename__ = 'cluster_plugins'
@ -50,6 +50,105 @@ class ClusterPlugins(Base):
attributes = Column(MutableDict.as_mutable(JSON),
nullable=False,
server_default='{}')
cluster = relationship("Cluster", backref=backref(
"cluster_plugins", cascade="delete"))
plugin = relationship("Plugin", backref=backref(
"cluster_plugins", cascade="delete"))
class NodeNICInterfaceClusterPlugin(Base):
"""Operates with NIC data from plugins
Example (fetch plugin data for specific interaface):
db().query(
NodeNICInterfaceClusterPlugin
).join(
models.ClusterPlugin,
).filter(
NodeNICInterfaceClusterPlugin.interface_id == interface_id
).filter(
models.ClusterPlugin.enabled.is_(True)).all()
"""
__tablename__ = 'node_nic_interface_cluster_plugins'
id = Column(Integer, primary_key=True)
cluster_plugin_id = Column(
Integer,
ForeignKey('cluster_plugins.id', ondelete='CASCADE'),
nullable=False)
interface_id = Column(
Integer,
ForeignKey('node_nic_interfaces.id', ondelete='CASCADE'),
nullable=False)
node_id = Column(
Integer,
ForeignKey('nodes.id', ondelete='CASCADE'),
nullable=False)
attributes = Column(
MutableDict.as_mutable(JSON),
nullable=False,
server_default='{}')
node = relationship("Node", backref=backref(
"node_nic_interface_cluster_plugins", cascade="delete"))
class NodeBondInterfaceClusterPlugin(Base):
"""Operates with Bond data from plugins
Example (fetch plugin data for specific bond):
db().query(
NodeBondInterfaceClusterPlugin
).join(
models.ClusterPlugin,
).filter(
NodeBondInterfaceClusterPlugin.bond_id == bond_id
).filter(
models.ClusterPlugin.enabled.is_(True)).all()
"""
__tablename__ = 'node_bond_interface_cluster_plugins'
id = Column(Integer, primary_key=True)
cluster_plugin_id = Column(
Integer,
ForeignKey('cluster_plugins.id', ondelete='CASCADE'),
nullable=False)
bond_id = Column(
Integer,
ForeignKey('node_bond_interfaces.id', ondelete='CASCADE'),
nullable=False)
node_id = Column(
Integer,
ForeignKey('nodes.id', ondelete='CASCADE'),
nullable=False)
attributes = Column(
MutableDict.as_mutable(JSON),
nullable=False,
server_default='{}')
node = relationship("Node", backref=backref(
"node_bond_interface_cluster_plugins", cascade="delete"))
class NodeClusterPlugin(Base):
__tablename__ = 'node_cluster_plugins'
id = Column(Integer, primary_key=True)
cluster_plugin_id = Column(
Integer,
ForeignKey('cluster_plugins.id', ondelete='CASCADE'),
nullable=False)
node_id = Column(
Integer,
ForeignKey('nodes.id', ondelete='CASCADE'),
nullable=False)
attributes = Column(
MutableDict.as_mutable(JSON),
nullable=False,
server_default='{}')
node = relationship("Node", backref=backref(
"node_cluster_plugins", cascade="delete"))
class Plugin(Base):
@ -83,6 +182,12 @@ class Plugin(Base):
MutableDict.as_mutable(JSON), server_default='{}', nullable=False)
network_roles_metadata = Column(
MutableList.as_mutable(JSON), server_default='[]', nullable=False)
nic_attributes_metadata = Column(
MutableDict.as_mutable(JSON), server_default='{}', nullable=False)
bond_attributes_metadata = Column(
MutableDict.as_mutable(JSON), server_default='{}', nullable=False)
node_attributes_metadata = Column(
MutableDict.as_mutable(JSON), server_default='{}', nullable=False)
components_metadata = Column(
MutableList.as_mutable(JSON), server_default='[]')
# TODO(apopovych): To support old plugins versions we need separate
@ -98,7 +203,7 @@ class Plugin(Base):
tasks = Column(
MutableList.as_mutable(JSON), server_default='[]', nullable=False)
clusters = relationship("Cluster",
secondary=ClusterPlugins.__table__,
secondary=ClusterPlugin.__table__,
backref="plugins")
links = relationship(
"PluginLink", backref="plugin", cascade="delete")

View File

@ -70,6 +70,10 @@ class Release(Base):
default=[], nullable=False, server_default='{}')
node_attributes = Column(MutableDict.as_mutable(JSON), default={},
server_default='{}', nullable=False)
nic_attributes = Column(MutableDict.as_mutable(JSON), default={},
server_default='{}', nullable=False)
bond_attributes = Column(MutableDict.as_mutable(JSON), default={},
server_default='{}', nullable=False)
# TODO(enchantner): get rid of properties

View File

@ -156,6 +156,12 @@ class NodeNICInterface(Base):
offloading_modes = Column(MutableList.as_mutable(JSON),
default=[], nullable=False,
server_default='[]')
attributes = Column(
MutableDict.as_mutable(JSON),
default={}, server_default='{}', nullable=False)
meta = Column(
MutableDict.as_mutable(JSON),
default={}, server_default='{}', nullable=False)
@property
def type(self):
@ -220,6 +226,9 @@ class NodeBondInterface(Base):
MutableDict.as_mutable(JSON), default={}, nullable=False,
server_default='{}')
slaves = relationship("NodeNICInterface", backref="bond")
attributes = Column(
MutableDict.as_mutable(JSON),
default={}, server_default='{}', nullable=False)
@property
def max_speed(self):

View File

@ -75,7 +75,7 @@ from nailgun.objects.node_group import NodeGroupCollection
from nailgun.objects.plugin import Plugin
from nailgun.objects.plugin import PluginCollection
from nailgun.objects.plugin import ClusterPlugins
from nailgun.objects.plugin import ClusterPlugin
from nailgun.objects.plugin_link import PluginLink
from nailgun.objects.plugin_link import PluginLinkCollection

View File

@ -41,7 +41,7 @@ from nailgun.logger import logger
from nailgun.objects import DeploymentGraph
from nailgun.objects import NailgunCollection
from nailgun.objects import NailgunObject
from nailgun.objects.plugin import ClusterPlugins
from nailgun.objects.plugin import ClusterPlugin
from nailgun.objects import Release
from nailgun.objects.serializers.cluster import ClusterSerializer
from nailgun.plugins.manager import PluginManager
@ -185,7 +185,7 @@ class Cluster(NailgunObject):
cls.add_pending_changes(
cluster, consts.CLUSTER_CHANGES.vmware_attributes)
ClusterPlugins.add_compatible_plugins(cluster)
ClusterPlugin.add_compatible_plugins(cluster)
PluginManager.enable_plugins_by_components(cluster)
fire_callback_on_cluster_create(cluster, data)

View File

@ -48,7 +48,7 @@ class Plugin(NailgunObject):
plugin_adapter = wrap_plugin(new_plugin)
cls.update(new_plugin, plugin_adapter.get_metadata())
ClusterPlugins.add_compatible_clusters(new_plugin)
ClusterPlugin.add_compatible_clusters(new_plugin)
return new_plugin
@ -132,9 +132,9 @@ class PluginCollection(NailgunCollection):
return sorted(release_plugins, key=lambda plugin: plugin.name)
class ClusterPlugins(NailgunObject):
class ClusterPlugin(NailgunObject):
model = models.ClusterPlugins
model = models.ClusterPlugin
@classmethod
def is_compatible(cls, cluster, plugin):

View File

@ -286,7 +286,7 @@ class DeploymentMultinodeSerializer(object):
:param attributes: the serialized attributes
:param cluster: the cluster object
"""
plugins = objects.ClusterPlugins.get_enabled(cluster.id)
plugins = objects.ClusterPlugin.get_enabled(cluster.id)
attributes['plugins'] = [
self.serialize_plugin(cluster, p) for p in plugins
]

View File

@ -164,6 +164,18 @@ class PluginAdapterBase(object):
def components_metadata(self):
return self.plugin.components_metadata
@property
def bond_attributes_metadata(self):
return self.plugin.bond_attributes_metadata
@property
def nic_attributes_metadata(self):
return self.plugin.bond_attributes_metadata
@property
def node_attributes_metadata(self):
return self.plugin.node_attributes_metadata
@property
def releases(self):
return self.plugin.releases
@ -360,6 +372,12 @@ class PluginAdapterV4(PluginAdapterV3):
class PluginAdapterV5(PluginAdapterV4):
"""Plugin wrapper class for package version 5.0.0"""
def __init__(self, plugin):
super(PluginAdapterV5, self).__init__(plugin)
self.db_cfg_mapping['nic_attributes_metadata'] = 'nic_config.yaml'
self.db_cfg_mapping['bond_attributes_metadata'] = 'bond_config.yaml'
self.db_cfg_mapping['node_attributes_metadata'] = 'node_config.yaml'
__version_mapping = {
'1.0.': PluginAdapterV1,

View File

@ -18,7 +18,7 @@ from six.moves import map
from nailgun import errors
from nailgun.logger import logger
from nailgun.objects.plugin import ClusterPlugins
from nailgun.objects.plugin import ClusterPlugin
from nailgun.objects.plugin import Plugin
from nailgun.objects.plugin import PluginCollection
from nailgun.plugins.adapters import wrap_plugin
@ -46,7 +46,6 @@ class PluginManager(object):
for k in list(attributes):
if cls.is_plugin_data(attributes[k]):
plugins[k] = attributes.pop(k)['metadata']
cluster.attributes.editable.pop(k, None)
for container in six.itervalues(plugins):
default = container.get('default', False)
@ -60,7 +59,7 @@ class PluginManager(object):
continue
enabled = container['enabled']\
and plugin_id == container['chosen_id']
ClusterPlugins.set_attributes(
ClusterPlugin.set_attributes(
cluster.id, plugin.id, enabled=enabled,
attrs=attrs if enabled or default else None
)
@ -80,7 +79,7 @@ class PluginManager(object):
:rtype: dict
"""
plugins_attributes = {}
for plugin in ClusterPlugins.get_connected_plugins_data(cluster.id):
for plugin in ClusterPlugin.get_connected_plugins_data(cluster.id):
db_plugin = Plugin.get_by_uid(plugin.id)
plugin_adapter = wrap_plugin(db_plugin)
default_attrs = plugin_adapter.attributes_metadata
@ -182,7 +181,7 @@ class PluginManager(object):
@classmethod
def get_enabled_plugins(cls, cluster):
return [wrap_plugin(plugin)
for plugin in ClusterPlugins.get_enabled(cluster.id)]
for plugin in ClusterPlugin.get_enabled(cluster.id)]
@classmethod
def get_network_roles(cls, cluster, merge_policy):
@ -196,7 +195,7 @@ class PluginManager(object):
all_roles = dict((role['id'], role) for role in instance_roles)
conflict_roles = dict()
for plugin in ClusterPlugins.get_enabled(cluster.id):
for plugin in ClusterPlugin.get_enabled(cluster.id):
for role in plugin.network_roles_metadata:
role_id = role['id']
if role_id in all_roles:
@ -226,7 +225,7 @@ class PluginManager(object):
deployment_tasks = []
processed_tasks = {}
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
enabled_plugins = ClusterPlugin.get_enabled(cluster.id)
for plugin_adapter in map(wrap_plugin, enabled_plugins):
depl_tasks = plugin_adapter.get_deployment_tasks(graph_type)
@ -252,7 +251,7 @@ class PluginManager(object):
result = {}
core_roles = set(cluster.release.roles_metadata)
for plugin_db in ClusterPlugins.get_enabled(cluster.id):
for plugin_db in ClusterPlugin.get_enabled(cluster.id):
plugin_roles = wrap_plugin(plugin_db).normalized_roles_metadata
# we should check all possible cases of roles intersection
@ -293,7 +292,7 @@ class PluginManager(object):
release_volumes_ids = [v['id'] for v in release_volumes]
processed_volumes = {}
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
enabled_plugins = ClusterPlugin.get_enabled(cluster.id)
for plugin_adapter in map(wrap_plugin, enabled_plugins):
metadata = plugin_adapter.volumes_metadata
@ -383,7 +382,7 @@ class PluginManager(object):
cluster_components = set(cluster.components)
plugin_ids = [p.id for p in PluginCollection.all_newest()]
for plugin in ClusterPlugins.get_connected_plugins(
for plugin in ClusterPlugin.get_connected_plugins(
cluster, plugin_ids):
plugin_adapter = wrap_plugin(plugin)
plugin_components = set(
@ -391,7 +390,7 @@ class PluginManager(object):
for component in plugin_adapter.components_metadata)
if cluster_components & plugin_components:
ClusterPlugins.set_attributes(
ClusterPlugin.set_attributes(
cluster.id, plugin.id, enabled=True)
@classmethod

View File

@ -38,7 +38,7 @@ from nailgun.db.sqlalchemy.models import Node
from nailgun.db.sqlalchemy.models import Release
from nailgun.extensions.network_manager import connectivity_check
from nailgun.extensions.network_manager import utils as net_utils
from nailgun.objects.plugin import ClusterPlugins
from nailgun.objects.plugin import ClusterPlugin
from nailgun.task.helpers import TaskHelper
from nailgun.utils import logs as logs_utils
from nailgun.utils import reverse
@ -600,7 +600,7 @@ class NailgunReceiver(object):
if task.name != consts.TASK_NAMES.provision:
plugins_msg = cls._make_plugins_success_message(
ClusterPlugins.get_enabled(task.cluster.id))
ClusterPlugin.get_enabled(task.cluster.id))
if plugins_msg:
message = '{0}\n\n{1}'.format(message, plugins_msg)

View File

@ -23,7 +23,7 @@ from nailgun.objects import Cluster
from nailgun.objects import ClusterCollection
from nailgun.objects import MasterNodeSettings
from nailgun.objects import NodeCollection
from nailgun.objects.plugin import ClusterPlugins
from nailgun.objects.plugin import ClusterPlugin
from nailgun.settings import settings
from nailgun.statistics.utils import get_attr_value
from nailgun.statistics.utils import WhiteListRule
@ -179,6 +179,12 @@ class InstallationInfo(object):
WhiteListRule(('network_roles_metadata',),
'network_roles_metadata', None),
WhiteListRule(('components_metadata',), 'components_metadata', None),
WhiteListRule(
('nic_attributes_metadata',), 'nic_attributes_metadata', None),
WhiteListRule(
('bond_attributes_metadata',), 'bond_attributes_metadata', None),
WhiteListRule(
('node_attributes_metadata',), 'node_attributes_metadata', None),
WhiteListRule(('deployment_tasks',), 'deployment_tasks', None),
WhiteListRule(('tasks',), 'tasks', None),
)
@ -286,14 +292,15 @@ class InstallationInfo(object):
'network_configuration': self.get_network_configuration_info(
cluster),
'installed_plugins': self.get_cluster_plugins_info(cluster),
'components': cluster.components
'components': cluster.components,
'cluster_plugins': cluster.cluster_plugins
}
clusters_info.append(cluster_info)
return clusters_info
def get_cluster_plugins_info(self, cluster):
plugins_info = []
for plugin_inst in ClusterPlugins.get_enabled(cluster.id):
for plugin_inst in ClusterPlugin.get_enabled(cluster.id):
plugin_info = self.get_attributes(plugin_inst.__dict__,
self.plugin_info_white_list)
plugins_info.append(plugin_info)

View File

@ -65,7 +65,7 @@ from nailgun.db.sqlalchemy.models import Task
# here come objects
from nailgun.objects import Cluster
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.objects import MasterNodeSettings
from nailgun.objects import NetworkGroup
from nailgun.objects import Node
@ -542,6 +542,9 @@ class EnvironmentManager(object):
deployment_tasks = plugin_data.pop('deployment_tasks', None)
tasks = plugin_data.pop('tasks', None)
components = plugin_data.pop('components', None)
nic_config = plugin_data.pop('nic_config', None)
bond_config = plugin_data.pop('bond_config', None)
node_config = plugin_data.pop('node_config', None)
mocked_metadata = {
'metadata.yaml': plugin_data,
@ -551,7 +554,10 @@ class EnvironmentManager(object):
'network_roles.yaml': network_roles,
'deployment_tasks.yaml': deployment_tasks,
'tasks.yaml': tasks,
'components.yaml': components
'components.yaml': components,
'nic_config.yaml': nic_config,
'bond_config.yaml': bond_config,
'node_config.yaml': node_config
}
m_load_conf.side_effect = lambda key: copy.deepcopy(
@ -572,7 +578,7 @@ class EnvironmentManager(object):
# Enable plugin for specific cluster
if cluster:
cluster.plugins.append(plugin)
ClusterPlugins.set_attributes(
ClusterPlugin.set_attributes(
cluster.id, plugin.id, enabled=enabled,
attrs=plugin.attributes_metadata or {}
)
@ -751,6 +757,48 @@ class EnvironmentManager(object):
'weight': kwargs.get('weight', 25),
'label': kwargs.get('label', 'label')}}}
def get_default_plugin_nic_config(self, **kwargs):
nic_attributes = {
'plugin_name_text': {
'value': 'value',
'type': 'text',
'description': 'Some description',
'weight': 25,
'label': 'label'
}
}
nic_attributes.update(kwargs)
return nic_attributes
def get_default_plugin_bond_config(self, **kwargs):
bond_attributes = {
'plugin_name_text': {
'value': 'value',
'type': 'text',
'description': 'Some description',
'weight': 25,
'label': 'label'
}
}
bond_attributes.update(kwargs)
return bond_attributes
def get_default_plugin_node_config(self, **kwargs):
node_attributes = {
'plugin_name_text': {
'value': 'value',
'type': 'text',
'description': 'Some description',
'weight': 25,
'label': 'label'
}
}
node_attributes.update(kwargs)
return node_attributes
def get_default_plugin_node_roles_config(self, **kwargs):
node_roles = {
'testing_plugin': {

View File

@ -140,9 +140,9 @@ class TestClusterRolesHandler(base.BaseTestCase):
plugin_data['roles_metadata'] = self.ROLES
plugin = objects.Plugin.create(plugin_data)
self.cluster.plugins.append(plugin)
objects.ClusterPlugins.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
objects.ClusterPlugin.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
self.db.flush()
roles = self.app.get(
@ -164,9 +164,9 @@ class TestClusterRolesHandler(base.BaseTestCase):
plugin_data['volumes_metadata'] = self.VOLUMES
plugin = objects.Plugin.create(plugin_data)
self.cluster.plugins.append(plugin)
objects.ClusterPlugins.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
objects.ClusterPlugin.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
self.db.flush()
plugin_adapter = adapters.wrap_plugin(plugin)

View File

@ -19,7 +19,7 @@ import uuid
from nailgun import consts
from nailgun import errors
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.plugins.adapters import PluginAdapterV3
from nailgun.plugins.manager import PluginManager
from nailgun.test import base
@ -282,7 +282,7 @@ class TestPluginManager(base.BaseIntegrationTest):
'hypervisor:test_hypervisor',
'storage:test_storage']})
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
enabled_plugins = ClusterPlugin.get_enabled(cluster.id)
self.assertItemsEqual([plugin], enabled_plugins)
def test_get_plugins_attributes_when_cluster_is_locked(self):
@ -530,7 +530,7 @@ class TestClusterPluginIntegration(base.BaseTestCase):
plugin_a = self._create_plugin(**self._compat_meta)
self._create_plugin(**self._uncompat_meta)
compat_plugins = ClusterPlugins.get_compatible_plugins(self.cluster)
compat_plugins = ClusterPlugin.get_compatible_plugins(self.cluster)
self.assertItemsEqual(compat_plugins, [plugin_a])
def test_get_compatible_plugins_for_new_cluster(self):
@ -544,18 +544,18 @@ class TestClusterPluginIntegration(base.BaseTestCase):
'mode': consts.CLUSTER_MODES.ha_compact,
})
compat_plugins = ClusterPlugins.get_compatible_plugins(cluster)
compat_plugins = ClusterPlugin.get_compatible_plugins(cluster)
self.assertItemsEqual(compat_plugins, [plugin_a, plugin_b])
def test_get_enabled_plugins(self):
plugin_a = self._create_plugin(**self._compat_meta)
plugin_b = self._create_plugin(**self._compat_meta)
ClusterPlugins.set_attributes(
ClusterPlugin.set_attributes(
self.cluster.id, plugin_a.id, enabled=True)
compat_plugins = ClusterPlugins.get_compatible_plugins(self.cluster)
compat_plugins = ClusterPlugin.get_compatible_plugins(self.cluster)
self.assertItemsEqual(compat_plugins, [plugin_a, plugin_b])
enabled_plugins = ClusterPlugins.get_enabled(self.cluster.id)
enabled_plugins = ClusterPlugin.get_enabled(self.cluster.id)
self.assertItemsEqual(enabled_plugins, [plugin_a])

View File

@ -145,21 +145,21 @@ class TestPluginsApi(BasePluginTest):
cluster = self.create_cluster()
self.assertItemsEqual(
[],
objects.ClusterPlugins.get_enabled(cluster.id)
objects.ClusterPlugin.get_enabled(cluster.id)
)
resp = self.enable_plugin(cluster, plugin.name, plugin.id)
self.assertEqual(resp.status_code, 200)
self.assertItemsEqual(
[plugin],
objects.ClusterPlugins.get_enabled(cluster.id)
objects.ClusterPlugin.get_enabled(cluster.id)
)
resp = self.disable_plugin(cluster, plugin.name)
self.assertEqual(resp.status_code, 200)
self.assertItemsEqual(
[],
objects.ClusterPlugins.get_enabled(cluster.id)
objects.ClusterPlugin.get_enabled(cluster.id)
)
def test_delete_plugin(self):
@ -236,10 +236,10 @@ class TestPluginsApi(BasePluginTest):
return response.json_body['id']
def get_num_enabled(cluster_id):
return objects.ClusterPlugins.get_enabled(cluster_id).count()
return objects.ClusterPlugin.get_enabled(cluster_id).count()
def get_enabled_version(cluster_id):
plugin = objects.ClusterPlugins.get_enabled(cluster_id).first()
plugin = objects.ClusterPlugin.get_enabled(cluster_id).first()
return plugin.version
plugin_ids = []

View File

@ -520,7 +520,9 @@ class TestInstallationInfo(BaseTestCase):
# Related tables
'clusters', 'cluster_changes',
'nodegroups', 'ip_addrs', 'node_nic_interfaces',
'node_bond_interfaces', 'network_groups'
'node_bond_interfaces', 'network_groups',
'node_nic_interface_cluster_plugins',
'node_bond_interface_cluster_plugins', 'node_cluster_plugins'
)
for field in remove_fields:
node_schema.pop(field)

View File

@ -12,6 +12,8 @@
# 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
@ -68,10 +70,61 @@ def prepare():
meta.tables['releases'].insert(),
[{
'name': 'test_name',
'version': '2015.1-8.0',
'version': '2015.1-10.0',
'operating_system': 'ubuntu',
'state': 'available',
'deployment_tasks': jsonutils.dumps(JSON_TASKS),
'roles': jsonutils.dumps([
'controller',
'compute',
'virt',
'compute-vmware',
'ironic',
'cinder',
'cinder-block-device',
'cinder-vmware',
'ceph-osd',
'mongo',
'base-os',
]),
'roles_metadata': jsonutils.dumps({
'controller': {
'name': 'Controller',
},
'compute': {
'name': 'Compute',
},
'virt': {
'name': 'Virtual',
},
'compute-vmware': {
'name': 'Compute VMware',
},
'ironic': {
'name': 'Ironic',
},
'cinder': {
'name': 'Cinder',
},
'cinder-block-device': {
'name': 'Cinder Block Device',
},
'cinder-vmware': {
'name': 'Cinder Proxy to VMware Datastore',
},
'ceph-osd': {
'name': 'Ceph OSD',
},
'mongo': {
'name': 'Telemetry - MongoDB',
},
'base-os': {
'name': 'Operating System',
}
}),
'is_deployable': True
}])
release_id = result.inserted_primary_key[0]
cluster_ids = []
@ -85,10 +138,25 @@ def prepare():
'status': 'new',
'net_provider': 'neutron',
'grouping': 'roles',
'fuel_version': '10.0'
'fuel_version': '10.0',
'deployment_tasks': jsonutils.dumps(JSON_TASKS)
}])
cluster_ids.append(result.inserted_primary_key[0])
result = db.execute(
meta.tables['nodes'].insert(),
[{
'uuid': '26b508d0-0d76-4159-bce9-f67ec2765480',
'cluster_id': None,
'group_id': None,
'status': 'discover',
'meta': '{}',
'mac': 'aa:aa:aa:aa:aa:aa',
'timestamp': datetime.datetime.utcnow(),
}]
)
node_id = result.inserted_primary_key[0]
result = db.execute(
meta.tables['plugins'].insert(),
[{
@ -97,7 +165,7 @@ def prepare():
'version': '2.0.0',
'description': 'Test plugin A for Fuel',
'homepage': 'http://fuel_plugins.test_plugin.com',
'package_version': '4.0.0',
'package_version': '5.0.0',
'groups': jsonutils.dumps(['tgroup']),
'authors': jsonutils.dumps(['tauthor']),
'licenses': jsonutils.dumps(['tlicense']),
@ -105,7 +173,7 @@ def prepare():
{'repository_path': 'repositories/ubuntu'}
]),
'deployment_tasks': jsonutils.dumps(JSON_TASKS),
'fuel_version': jsonutils.dumps(['8.0']),
'fuel_version': jsonutils.dumps(['10.0']),
'network_roles_metadata': jsonutils.dumps([{
'id': 'admin/vip',
'default_mapping': 'fuelweb_admin',
@ -136,14 +204,14 @@ def prepare():
'version': '2.0.0',
'description': 'Test plugin B for Fuel',
'homepage': 'http://fuel_plugins.test_plugin.com',
'package_version': '4.0.0',
'package_version': '5.0.0',
'groups': jsonutils.dumps(['tgroup']),
'authors': jsonutils.dumps(['tauthor']),
'licenses': jsonutils.dumps(['tlicense']),
'releases': jsonutils.dumps([
{'repository_path': 'repositories/ubuntu'}
]),
'fuel_version': jsonutils.dumps(['8.0']),
'fuel_version': jsonutils.dumps(['10.0']),
'network_roles_metadata': jsonutils.dumps([{
'id': 'admin/vip',
'default_mapping': 'fuelweb_admin',
@ -196,6 +264,14 @@ def prepare():
]
)
db.execute(
meta.tables['cluster_plugins'].insert(),
[
{'cluster_id': cluster_ids[0], 'plugin_id': plugin_a_id},
{'cluster_id': cluster_ids[0], 'plugin_id': plugin_b_id}
]
)
db.execute(
meta.tables['plugin_links'].insert(),
[
@ -217,6 +293,36 @@ def prepare():
]
)
db.execute(
meta.tables['node_nic_interfaces'].insert(),
[{
'id': 1,
'node_id': node_id,
'name': 'test_interface',
'mac': '00:00:00:00:00:01',
'max_speed': 200,
'current_speed': 100,
'ip_addr': '10.20.0.2',
'netmask': '255.255.255.0',
'state': 'test_state',
'interface_properties': jsonutils.dumps(
{'test_property': 'test_value'}),
'driver': 'test_driver',
'bus_info': 'some_test_info'
}]
)
db.execute(
meta.tables['node_bond_interfaces'].insert(),
[{
'node_id': node_id,
'name': 'test_bond_interface',
'mode': 'active-backup',
'bond_properties': jsonutils.dumps(
{'test_property': 'test_value'})
}]
)
db.commit()
@ -235,3 +341,83 @@ class TestPluginLinksConstraints(base.BaseAlembicMigrationTest):
[sa.func.count(self.meta.tables['cluster_plugin_links'].c.id)]
)).fetchone()[0]
self.assertEqual(links_count, 2)
class TestPluginAttributesMigration(base.BaseAlembicMigrationTest):
def test_new_attributes_fields_exist(self):
node_bond_interfaces_table = self.meta.tables['node_bond_interfaces']
node_nic_interfaces_table = self.meta.tables['node_nic_interfaces']
plugins_table = self.meta.tables['plugins']
releases_table = self.meta.tables['releases']
columns = [
plugins_table.c.nic_attributes_metadata,
plugins_table.c.bond_attributes_metadata,
plugins_table.c.node_attributes_metadata,
node_bond_interfaces_table.c.attributes,
node_nic_interfaces_table.c.attributes,
node_nic_interfaces_table.c.meta,
releases_table.c.nic_attributes,
releases_table.c.bond_attributes
]
for column in columns:
db_values = db.execute(sa.select([column])).fetchone()
for db_value in db_values:
self.assertEqual(db_value, '{}')
def test_node_nic_interface_cluster_plugins_creation(self):
node_nic_interface_cluster_plugins = \
self.meta.tables['node_nic_interface_cluster_plugins']
cluster_plugins = self.meta.tables['cluster_plugins']
node_nic_interfaces = self.meta.tables['node_nic_interfaces']
nodes = self.meta.tables['nodes']
cluster_plugin_id = db.execute(sa.select([cluster_plugins])).scalar()
interface_id = db.execute(sa.select([node_nic_interfaces])).scalar()
node_id = db.execute(sa.select([nodes])).scalar()
db.execute(
node_nic_interface_cluster_plugins.insert(),
[{
'cluster_plugin_id': cluster_plugin_id,
'interface_id': interface_id,
'node_id': node_id,
'attributes': jsonutils.dumps({'test_attr': 'test'})
}])
def test_node_bond_interface_cluster_plugins_creation(self):
node_bond_interface_cluster_plugins = \
self.meta.tables['node_bond_interface_cluster_plugins']
cluster_plugins = self.meta.tables['cluster_plugins']
node_bond_interfaces = self.meta.tables['node_bond_interfaces']
nodes = self.meta.tables['nodes']
cluster_plugin_id = db.execute(sa.select([cluster_plugins])).scalar()
bond_id = db.execute(sa.select([node_bond_interfaces])).scalar()
node_id = db.execute(sa.select([nodes])).scalar()
db.execute(
node_bond_interface_cluster_plugins.insert(),
[{
'cluster_plugin_id': cluster_plugin_id,
'bond_id': bond_id,
'node_id': node_id,
'attributes': jsonutils.dumps({'test_attr': 'test'})
}])
def test_node_cluster_plugins_creation(self):
node_cluster_plugins = self.meta.tables['node_cluster_plugins']
cluster_plugins = self.meta.tables['cluster_plugins']
nodes = self.meta.tables['nodes']
cluster_plugin_id = db.execute(sa.select([cluster_plugins])).scalar()
node_id = db.execute(sa.select([nodes])).scalar()
db.execute(
node_cluster_plugins.insert(),
[{
'cluster_plugin_id': cluster_plugin_id,
'node_id': node_id,
'attributes': jsonutils.dumps({'test_attr': 'test'})
}])

View File

@ -385,7 +385,7 @@ class TestNeutronConfigInternalFloatingNames(base.BaseAlembicMigrationTest):
self.assertEqual('net04_ext', neutron_config['floating_name'])
class TestClusterPluginsMigration(base.BaseAlembicMigrationTest):
class TestClusterPluginMigration(base.BaseAlembicMigrationTest):
def _get_enabled(self, plugin_name):
plugins = self.meta.tables['plugins']

View File

@ -267,7 +267,7 @@ def prepare():
'editable': jsonutils.dumps(editable)
}])
db.execute(
result = db.execute(
meta.tables['nodes'].insert(),
[{
'uuid': '26b508d0-0d76-4159-bce9-f67ec2765480',

View File

@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from nailgun import consts
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.objects import Plugin
from nailgun.objects import PluginCollection
from nailgun.test import base
@ -93,7 +93,7 @@ class TestPluginCollection(ExtraFunctions):
self.assertNotEqual(plugin.name, 'incompatible_plugin')
class TestClusterPlugins(ExtraFunctions):
class TestClusterPlugin(ExtraFunctions):
def test_connect_to_cluster(self):
meta = base.reflect_db_metadata()
@ -109,8 +109,8 @@ class TestClusterPlugins(ExtraFunctions):
self._create_test_plugins()
cluster = self._create_test_cluster()
plugin = ClusterPlugins.get_connected_plugins(cluster).first()
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
plugin = ClusterPlugin.get_connected_plugins(cluster).first()
ClusterPlugin.set_attributes(cluster.id, plugin.id, enabled=True)
columns = meta.tables['cluster_plugins'].c
enabled = self.db.execute(
@ -124,21 +124,21 @@ class TestClusterPlugins(ExtraFunctions):
self._create_test_plugins()
cluster = self._create_test_cluster()
number_of_connected_plugins_data_items =\
ClusterPlugins.get_connected_plugins_data(cluster.id).count()
ClusterPlugin.get_connected_plugins_data(cluster.id).count()
self.assertEqual(7, number_of_connected_plugins_data_items)
def test_get_all_connected_plugins(self):
self._create_test_plugins()
cluster = self._create_test_cluster()
number_of_connected_plugins =\
ClusterPlugins.get_connected_plugins(cluster).count()
ClusterPlugin.get_connected_plugins(cluster).count()
self.assertEqual(7, number_of_connected_plugins)
def test_get_connected_for_specific_plugins(self):
plugin_ids = self._create_test_plugins()
cluster = self._create_test_cluster()
number_of_connected_plugins =\
ClusterPlugins.get_connected_plugins(
ClusterPlugin.get_connected_plugins(
cluster, plugin_ids[1:]).count()
self.assertEqual(6, number_of_connected_plugins)
@ -147,24 +147,24 @@ class TestClusterPlugins(ExtraFunctions):
for _ in range(2):
self._create_test_cluster()
number_of_connected_clusters =\
ClusterPlugins.get_connected_clusters(plugin_id).count()
ClusterPlugin.get_connected_clusters(plugin_id).count()
self.assertEqual(2, number_of_connected_clusters)
def test_get_enabled(self):
self._create_test_plugins()
cluster = self._create_test_cluster()
plugin = ClusterPlugins.get_connected_plugins(cluster).first()
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
plugin = ClusterPlugin.get_connected_plugins(cluster).first()
ClusterPlugin.set_attributes(cluster.id, plugin.id, enabled=True)
enabled_plugin = ClusterPlugins.get_enabled(cluster.id).first()
enabled_plugin = ClusterPlugin.get_enabled(cluster.id).first()
self.assertEqual(plugin.id, enabled_plugin.id)
def test_is_plugin_used(self):
self._create_test_plugins()
cluster = self._create_test_cluster()
plugin = ClusterPlugins.get_connected_plugins(cluster).first()
self.assertFalse(ClusterPlugins.is_plugin_used(plugin.id))
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
self.assertTrue(ClusterPlugins.is_plugin_used(plugin.id))
plugin = ClusterPlugin.get_connected_plugins(cluster).first()
self.assertFalse(ClusterPlugin.is_plugin_used(plugin.id))
ClusterPlugin.set_attributes(cluster.id, plugin.id, enabled=True)
self.assertTrue(ClusterPlugin.is_plugin_used(plugin.id))

View File

@ -1163,9 +1163,9 @@ class TestClusterObject(BaseTestCase):
for kw in plugins_kw_list:
plugin = objects.Plugin.create(kw)
cluster.plugins.append(plugin)
objects.ClusterPlugins.set_attributes(cluster.id,
plugin.id,
enabled=True)
objects.ClusterPlugin.set_attributes(cluster.id,
plugin.id,
enabled=True)
return cluster
def _get_network_role_metadata(self, **kwargs):
@ -1811,9 +1811,9 @@ class TestClusterObjectGetRoles(BaseTestCase):
roles_metadata=roles_metadata,
))
self.cluster.plugins.append(plugin)
objects.ClusterPlugins.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
objects.ClusterPlugin.set_attributes(self.cluster.id,
plugin.id,
enabled=True)
self.db.refresh(plugin)
return plugin

View File

@ -23,7 +23,7 @@ from nailgun import consts
from nailgun.db import db
from nailgun import errors
from nailgun.expression import Expression
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.objects import DeploymentGraph
from nailgun.objects import Plugin
from nailgun.plugins import adapters
@ -378,6 +378,81 @@ class TestPluginV4(TestPluginBase):
ValueError, self.plugin_adapter._load_tasks)
class TestPluginV5(TestPluginBase):
__test__ = True
package_version = '5.0.0'
def test_get_metadata(self):
plugin_metadata = self.env.get_default_plugin_metadata()
attributes_metadata = self.env.get_default_plugin_env_config()
nic_attributes_metadata = self.env.get_default_plugin_nic_config()
bond_attributes_metadata = self.env.get_default_plugin_bond_config()
node_attributes_metadata = self.env.get_default_plugin_node_config()
roles_metadata = self.env.get_default_plugin_node_roles_config()
volumes_metadata = self.env.get_default_plugin_volumes_config()
network_roles_metadata = self.env.get_default_network_roles_config()
deployment_tasks = self.env.get_default_plugin_deployment_tasks()
tasks = self.env.get_default_plugin_tasks()
components_metadata = self.env.get_default_components()
mocked_metadata = {
self._find_path('metadata'): plugin_metadata,
self._find_path('environment_config'): attributes_metadata,
self._find_path('node_roles'): roles_metadata,
self._find_path('volumes'): volumes_metadata,
self._find_path('network_roles'): network_roles_metadata,
self._find_path('deployment_tasks'): deployment_tasks,
self._find_path('tasks'): tasks,
self._find_path('components'): components_metadata,
self._find_path('nic_config'): nic_attributes_metadata,
self._find_path('bond_config'): bond_attributes_metadata,
self._find_path('node_config'): node_attributes_metadata
}
with mock.patch.object(
self.plugin_adapter, '_load_config') as load_conf:
load_conf.side_effect = lambda key: mocked_metadata[key]
Plugin.update(self.plugin, self.plugin_adapter.get_metadata())
for key, val in six.iteritems(plugin_metadata):
self.assertEqual(
getattr(self.plugin, key), val)
self.assertEqual(
self.plugin.attributes_metadata,
attributes_metadata['attributes'])
self.assertEqual(
self.plugin.roles_metadata, roles_metadata)
self.assertEqual(
self.plugin.volumes_metadata, volumes_metadata)
self.assertEqual(
self.plugin.tasks, tasks)
self.assertEqual(
self.plugin.components_metadata, components_metadata)
self.assertEqual(
self.plugin.nic_attributes_metadata,
nic_attributes_metadata)
self.assertEqual(
self.plugin.bond_attributes_metadata,
bond_attributes_metadata)
self.assertEqual(
self.plugin.node_attributes_metadata,
bond_attributes_metadata)
plugin_tasks = self.env.get_default_plugin_deployment_tasks()
self.assertGreater(len(plugin_tasks), 0)
for k, v in six.iteritems(plugin_tasks[0]):
# this field is updated by plugin adapter
if k is 'parameters':
v.update({
'cwd': '/etc/fuel/plugins/testing_plugin-0.1/'
})
self.assertEqual(
self.plugin_adapter.get_deployment_tasks()[0][k],
v)
class TestClusterCompatibilityValidation(base.BaseTestCase):
def setUp(self):
@ -396,7 +471,7 @@ class TestClusterCompatibilityValidation(base.BaseTestCase):
def validate_with_cluster(self, **kwargs):
cluster = self.cluster_mock(**kwargs)
return ClusterPlugins.is_compatible(cluster, self.plugin)
return ClusterPlugin.is_compatible(cluster, self.plugin)
def test_validation_ubuntu_ha(self):
self.assertTrue(self.validate_with_cluster(

View File

@ -19,7 +19,7 @@ from mock import patch
from nailgun import consts
from nailgun import errors
from nailgun.objects import ClusterPlugins
from nailgun.objects import ClusterPlugin
from nailgun.objects import Plugin
from nailgun.rpc.receiver import NailgunReceiver
from nailgun.test import base
@ -44,9 +44,9 @@ class TestNailgunReceiver(base.BaseTestCase):
self.plugin = Plugin.create(meta)
self.cluster.plugins.append(self.plugin)
ClusterPlugins.set_attributes(self.cluster.id,
self.plugin.id,
enabled=True)
ClusterPlugin.set_attributes(self.cluster.id,
self.plugin.id,
enabled=True)
self.task = self.env.create_task(
name=consts.TASK_NAMES.deployment,