Enable plugins by chosen components
Modifying POST /clusters for handling components data Enabling proper plugins by components from wizard Change-Id: If6c0e9c2cb41652def663ba3c8c95484bd3430e9 Implements: blueprint component-registry
This commit is contained in:
parent
2e761d177e
commit
4aaf0777b1
@ -16,6 +16,7 @@
|
||||
|
||||
from nailgun.api.v1.handlers import base
|
||||
from nailgun.objects import Release
|
||||
from nailgun.objects.serializers.release import ComponentSerializer
|
||||
|
||||
|
||||
class ComponentCollectionHandler(base.CollectionHandler):
|
||||
@ -29,7 +30,9 @@ class ComponentCollectionHandler(base.CollectionHandler):
|
||||
* 404 (release not found in db)
|
||||
"""
|
||||
release = self.get_object_or_404(Release, release_id)
|
||||
return Release.get_all_components(release)
|
||||
components = Release.get_all_components(release)
|
||||
|
||||
return [ComponentSerializer.serialize(c) for c in components]
|
||||
|
||||
def POST(self, release_id):
|
||||
"""Creating of components is disallowed
|
||||
|
@ -18,6 +18,11 @@ from nailgun import consts
|
||||
|
||||
from nailgun.api.v1.validators.json_schema import base_types
|
||||
|
||||
COMPONENTS_TYPES_STR = '|'.join(
|
||||
['hypervisor', 'network', 'storage', 'additional_service'])
|
||||
COMPONENT_NAME_PATTERN = \
|
||||
'^({0}):([0-9a-z_-]+:)*[0-9a-z_-]+$'.format(COMPONENTS_TYPES_STR)
|
||||
|
||||
CLUSTER_UI_SETTINGS = {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -88,17 +93,19 @@ single_schema = {
|
||||
"type": "string",
|
||||
"enum": list(consts.CLUSTER_STATUSES)
|
||||
},
|
||||
"net_provider": {
|
||||
"type": "string",
|
||||
"enum": list(consts.CLUSTER_NET_PROVIDERS)
|
||||
},
|
||||
"ui_settings": CLUSTER_UI_SETTINGS,
|
||||
"release_id": {"type": "number"},
|
||||
"pending_release_id": base_types.NULLABLE_ID,
|
||||
"replaced_deployment_info": {"type": "object"},
|
||||
"replaced_provisioning_info": {"type": "object"},
|
||||
"is_customized": {"type": "boolean"},
|
||||
"fuel_version": {"type": "string"}
|
||||
"fuel_version": {"type": "string"},
|
||||
"components": {
|
||||
'type': 'array',
|
||||
'items': [{
|
||||
'type': 'string',
|
||||
'pattern': COMPONENT_NAME_PATTERN}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,14 @@ def upgrade_with_components():
|
||||
server_default='[]'
|
||||
)
|
||||
)
|
||||
op.add_column(
|
||||
'clusters',
|
||||
sa.Column(
|
||||
'components',
|
||||
fields.JSON(),
|
||||
nullable=False,
|
||||
server_default='[]')
|
||||
)
|
||||
|
||||
|
||||
def create_openstack_configs_table():
|
||||
@ -547,6 +555,7 @@ def downgrade_add_baremetal_net():
|
||||
|
||||
|
||||
def downgrade_with_components():
|
||||
op.drop_column('clusters', 'components')
|
||||
op.drop_column('plugins', 'components_metadata')
|
||||
op.drop_column('releases', 'components_metadata')
|
||||
|
||||
|
@ -34,6 +34,7 @@ from nailgun import consts
|
||||
from nailgun.db import db
|
||||
from nailgun.db.sqlalchemy.models.base import Base
|
||||
from nailgun.db.sqlalchemy.models.fields import JSON
|
||||
from nailgun.db.sqlalchemy.models.mutable import MutableList
|
||||
from nailgun.db.sqlalchemy.models.node import Node
|
||||
|
||||
|
||||
@ -113,6 +114,11 @@ class Cluster(Base):
|
||||
is_customized = Column(Boolean, default=False)
|
||||
fuel_version = Column(Text, nullable=False)
|
||||
deployment_tasks = Column(JSON, default=[])
|
||||
components = Column(
|
||||
MutableList.as_mutable(JSON),
|
||||
default=[],
|
||||
server_default='[]',
|
||||
nullable=False)
|
||||
extensions = Column(psql.ARRAY(String(consts.EXTENSION_NAME_MAX_SIZE)),
|
||||
default=[], nullable=False, server_default='{}')
|
||||
|
||||
|
@ -154,35 +154,38 @@ class Cluster(NailgunObject):
|
||||
assign_nodes = data.pop("nodes", [])
|
||||
|
||||
data["fuel_version"] = settings.VERSION["release"]
|
||||
new_cluster = super(Cluster, cls).create(data)
|
||||
cls.create_default_group(new_cluster)
|
||||
cluster = super(Cluster, cls).create(data)
|
||||
cls.create_default_group(cluster)
|
||||
|
||||
cls.create_attributes(new_cluster)
|
||||
cls.create_vmware_attributes(new_cluster)
|
||||
cls.create_default_extensions(new_cluster)
|
||||
cls.create_attributes(cluster)
|
||||
cls.create_vmware_attributes(cluster)
|
||||
cls.create_default_extensions(cluster)
|
||||
|
||||
try:
|
||||
cls.get_network_manager(new_cluster).\
|
||||
create_network_groups_and_config(new_cluster, data)
|
||||
cls.add_pending_changes(new_cluster, "attributes")
|
||||
cls.add_pending_changes(new_cluster, "networks")
|
||||
cls.add_pending_changes(new_cluster, "vmware_attributes")
|
||||
cls.get_network_manager(cluster).\
|
||||
create_network_groups_and_config(cluster, data)
|
||||
cls.add_pending_changes(
|
||||
cluster, consts.CLUSTER_CHANGES.attributes)
|
||||
cls.add_pending_changes(
|
||||
cluster, consts.CLUSTER_CHANGES.networks)
|
||||
cls.add_pending_changes(
|
||||
cluster, consts.CLUSTER_CHANGES.vmware_attributes)
|
||||
|
||||
if assign_nodes:
|
||||
cls.update_nodes(new_cluster, assign_nodes)
|
||||
cls.update_nodes(cluster, assign_nodes)
|
||||
except (
|
||||
errors.OutOfVLANs,
|
||||
errors.OutOfIPs,
|
||||
errors.NoSuitableCIDR
|
||||
) as exc:
|
||||
db().delete(new_cluster)
|
||||
raise errors.CannotCreate(exc.message)
|
||||
|
||||
db().flush()
|
||||
|
||||
ClusterPlugins.add_compatible_plugins(new_cluster)
|
||||
ClusterPlugins.add_compatible_plugins(cluster)
|
||||
PluginManager.enable_plugins_by_components(cluster)
|
||||
|
||||
return new_cluster
|
||||
return cluster
|
||||
|
||||
@classmethod
|
||||
def delete(cls, instance):
|
||||
|
@ -246,12 +246,12 @@ class ClusterPlugins(NailgunObject):
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def get_connected_plugins(cls, cluster_id):
|
||||
"""Returns plugins connected with given cluster.
|
||||
def get_connected_plugins_data(cls, cluster_id):
|
||||
"""Returns plugins and cluster_plugins data connected with cluster.
|
||||
|
||||
:param cluster_id: Cluster ID
|
||||
:type cluster_id: int
|
||||
:returns: List of plugins
|
||||
:returns: List of mixed data from plugins and cluster_plugins
|
||||
:rtype: iterable (SQLAlchemy query)
|
||||
"""
|
||||
return db().query(
|
||||
@ -264,8 +264,29 @@ class ClusterPlugins(NailgunObject):
|
||||
cls.model.attributes
|
||||
).join(cls.model)\
|
||||
.filter(cls.model.cluster_id == cluster_id)\
|
||||
.order_by(models.Plugin.name)\
|
||||
.order_by(models.Plugin.version)
|
||||
.order_by(models.Plugin.name, models.Plugin.version)
|
||||
|
||||
@classmethod
|
||||
def get_connected_plugins(cls, cluster, plugin_ids=None):
|
||||
"""Returns plugins connected with given cluster.
|
||||
|
||||
:param cluster: Cluster instance
|
||||
:type cluster: Cluster SQLAlchemy model
|
||||
:param plugin_ids: List of specific plugins ids to chose from
|
||||
:type plugin_ids: list
|
||||
:returns: List of plugins
|
||||
:rtype: iterable (SQLAlchemy query)
|
||||
"""
|
||||
plugins = db().query(
|
||||
models.Plugin
|
||||
).join(cls.model)\
|
||||
.filter(cls.model.cluster_id == cluster.id)\
|
||||
.order_by(models.Plugin.name, models.Plugin.version)
|
||||
|
||||
if plugin_ids:
|
||||
plugins = plugins.filter(cls.model.plugin_id.in_(plugin_ids))
|
||||
|
||||
return plugins
|
||||
|
||||
@classmethod
|
||||
def get_connected_clusters(cls, plugin_id):
|
||||
|
@ -32,6 +32,7 @@ class ClusterSerializer(BasicSerializer):
|
||||
"fuel_version",
|
||||
"pending_release_id",
|
||||
"is_locked",
|
||||
"components"
|
||||
)
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ class ReleaseSerializer(BasicSerializer):
|
||||
"state",
|
||||
"attributes_metadata",
|
||||
"vmware_attributes_metadata",
|
||||
"components_metadata"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ -43,3 +44,12 @@ class ReleaseSerializer(BasicSerializer):
|
||||
release_dict["is_deployable"] = Release.is_deployable(instance)
|
||||
|
||||
return release_dict
|
||||
|
||||
|
||||
class ComponentSerializer(BasicSerializer):
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, instance):
|
||||
instance.pop('bind', None)
|
||||
|
||||
return instance
|
||||
|
@ -115,7 +115,8 @@ class PluginManager(object):
|
||||
|
||||
plugins_attributes = {}
|
||||
for pid, name, title, version, enabled, default_attrs, cluster_attrs\
|
||||
in ClusterPlugins.get_connected_plugins(cluster.id):
|
||||
in ClusterPlugins.get_connected_plugins_data(cluster.id):
|
||||
|
||||
if all_versions:
|
||||
enabled = enabled and not default
|
||||
data = plugins_attributes.get(name, {})
|
||||
@ -346,3 +347,25 @@ class PluginManager(object):
|
||||
for plugin in plugins:
|
||||
plugin_adapter = wrap_plugin(plugin)
|
||||
plugin_adapter.sync_metadata_to_db()
|
||||
|
||||
@classmethod
|
||||
def enable_plugins_by_components(cls, cluster):
|
||||
"""Enable plugin by components
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: Cluster model
|
||||
:return: None
|
||||
"""
|
||||
cluster_components = set(cluster.components)
|
||||
plugin_ids = set(p.id for p in PluginCollection.all_newest())
|
||||
|
||||
for plugin in ClusterPlugins.get_connected_plugins(
|
||||
cluster, plugin_ids):
|
||||
plugin_adapter = wrap_plugin(plugin)
|
||||
plugin_components = set(
|
||||
component['name']
|
||||
for component in plugin_adapter.components_metadata)
|
||||
|
||||
for component in cluster_components & plugin_components:
|
||||
ClusterPlugins.set_attributes(
|
||||
cluster.id, plugin.id, enabled=True)
|
||||
|
@ -204,7 +204,8 @@ class InstallationInfo(object):
|
||||
'is_customized': cluster.is_customized,
|
||||
'network_configuration': self.get_network_configuration_info(
|
||||
cluster),
|
||||
'installed_plugins': self.get_cluster_plugins_info(cluster)
|
||||
'installed_plugins': self.get_cluster_plugins_info(cluster),
|
||||
'components': cluster.components
|
||||
}
|
||||
clusters_info.append(cluster_info)
|
||||
return clusters_info
|
||||
|
@ -39,7 +39,8 @@ class TestComponentHandler(base.BaseIntegrationTest):
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}],
|
||||
components_metadata=self.env.get_default_components(
|
||||
name='storage:test_component_2'))
|
||||
name='storage:test_component_2',
|
||||
bind='some_action_to_process'))
|
||||
|
||||
def test_get_components(self):
|
||||
resp = self.app.get(
|
||||
|
@ -227,6 +227,48 @@ class TestPluginManager(base.BaseIntegrationTest):
|
||||
expected_message):
|
||||
PluginManager.get_components_metadata(self.release)
|
||||
|
||||
def test_enable_plugins_by_component(self):
|
||||
self.env.create_plugin(
|
||||
name='plugin_with_test_storage',
|
||||
package_version='4.0.0',
|
||||
fuel_version=['8.0'],
|
||||
releases=[{
|
||||
'repository_path': 'repositories/ubuntu',
|
||||
'version': '2015.1-8.3',
|
||||
'os': 'ubuntu',
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}],
|
||||
components_metadata=self.env.get_default_components(
|
||||
name='storage:test_storage'))
|
||||
|
||||
plugin = self.env.create_plugin(
|
||||
version='1.0.0',
|
||||
name='plugin_with_test_storage',
|
||||
package_version='4.0.0',
|
||||
fuel_version=['8.0'],
|
||||
releases=[{
|
||||
'repository_path': 'repositories/ubuntu',
|
||||
'version': '2015.1-8.3',
|
||||
'os': 'ubuntu',
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}],
|
||||
components_metadata=self.env.get_default_components(
|
||||
name='storage:test_storage'))
|
||||
|
||||
cluster = self.env.create(
|
||||
release_kwargs={
|
||||
'operating_system': consts.RELEASE_OS.ubuntu,
|
||||
'version': '2015.1-8.3'},
|
||||
cluster_kwargs={
|
||||
'mode': consts.CLUSTER_MODES.ha_compact,
|
||||
'api': False,
|
||||
'components': [
|
||||
'hypervisor:test_hypervisor',
|
||||
'storage:test_storage']})
|
||||
|
||||
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
|
||||
self.assertItemsEqual([plugin], enabled_plugins)
|
||||
|
||||
|
||||
class TestClusterPluginIntegration(base.BaseTestCase):
|
||||
|
||||
|
@ -243,12 +243,6 @@ class TestReleaseMigrations(base.BaseAlembicMigrationTest):
|
||||
for state in states:
|
||||
self.assertEqual(state, 'manageonly')
|
||||
|
||||
def test_new_component_metadata_field_exists_and_empty(self):
|
||||
result = db.execute(
|
||||
sa.select([self.meta.tables['releases'].c.components_metadata]))
|
||||
self.assertEqual(
|
||||
jsonutils.loads(result.fetchone()[0]), [])
|
||||
|
||||
|
||||
class TestTaskStatus(base.BaseAlembicMigrationTest):
|
||||
|
||||
@ -447,13 +441,21 @@ class TestBaremetalFields(base.BaseAlembicMigrationTest):
|
||||
self.assertIn((baremetal_gateway, baremetal_range), result)
|
||||
|
||||
|
||||
class TestPluginMigration(base.BaseAlembicMigrationTest):
|
||||
class TestComponentsMigration(base.BaseAlembicMigrationTest):
|
||||
|
||||
def test_new_component_metadata_field_exists_and_empty(self):
|
||||
result = db.execute(
|
||||
sa.select([self.meta.tables['plugins'].c.components_metadata]))
|
||||
self.assertEqual(
|
||||
jsonutils.loads(result.fetchone()[0]), [])
|
||||
column_values = [
|
||||
(self.meta.tables['plugins'].c.components_metadata, []),
|
||||
(self.meta.tables['releases'].c.components_metadata, []),
|
||||
(self.meta.tables['clusters'].c.components, [])
|
||||
]
|
||||
|
||||
result = db.execute(sa.select(
|
||||
[item[0] for item in column_values]))
|
||||
db_values = result.fetchone()
|
||||
|
||||
for idx, db_value in enumerate(db_values):
|
||||
self.assertEqual(jsonutils.loads(db_value), column_values[idx][1])
|
||||
|
||||
|
||||
class TestMasterSettingsMigration(base.BaseAlembicMigrationTest):
|
||||
|
@ -114,38 +114,53 @@ class TestClusterPlugins(ExtraFunctions):
|
||||
self._create_test_plugins()
|
||||
cluster = self._create_test_cluster()
|
||||
|
||||
plugin_id = ClusterPlugins.get_connected_plugins(cluster.id)[0][0]
|
||||
ClusterPlugins.set_attributes(cluster.id, plugin_id, enabled=True)
|
||||
plugin = ClusterPlugins.get_connected_plugins(cluster)[0]
|
||||
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
|
||||
|
||||
columns = meta.tables['cluster_plugins'].c
|
||||
enabled = self.db.execute(
|
||||
sa.select([columns.enabled])
|
||||
.where(columns.cluster_id == cluster.id)
|
||||
.where(columns.plugin_id == plugin_id)
|
||||
.where(columns.plugin_id == plugin.id)
|
||||
).fetchone()
|
||||
self.assertTrue(enabled[0])
|
||||
|
||||
def test_get_connected_plugins(self):
|
||||
def test_get_connected_plugins_data(self):
|
||||
self._create_test_plugins()
|
||||
cluster = self._create_test_cluster()
|
||||
connected_plugins =\
|
||||
ClusterPlugins.get_connected_plugins(cluster.id).all()
|
||||
self.assertEqual(len(connected_plugins), 5)
|
||||
number_of_connected_plugins_data_items =\
|
||||
ClusterPlugins.get_connected_plugins_data(cluster.id).count()
|
||||
self.assertEqual(5, 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()
|
||||
self.assertEqual(5, 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(
|
||||
cluster, plugin_ids[1:]).count()
|
||||
self.assertEqual(4, number_of_connected_plugins)
|
||||
|
||||
def test_get_connected_clusters(self):
|
||||
plugin_id = self._create_test_plugins()[0]
|
||||
for _ in range(2):
|
||||
self._create_test_cluster()
|
||||
connected_clusters =\
|
||||
ClusterPlugins.get_connected_clusters(plugin_id).all()
|
||||
self.assertEqual(len(connected_clusters), 2)
|
||||
number_of_connected_clusters =\
|
||||
ClusterPlugins.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_id = ClusterPlugins.get_connected_plugins(cluster.id)[0][0]
|
||||
ClusterPlugins.set_attributes(cluster.id, plugin_id, enabled=True)
|
||||
plugin = ClusterPlugins.get_connected_plugins(cluster)[0]
|
||||
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
|
||||
|
||||
enabled_plugin = ClusterPlugins.get_enabled(cluster.id)[0].id
|
||||
self.assertEqual(enabled_plugin, plugin_id)
|
||||
self.assertEqual(enabled_plugin, plugin.id)
|
||||
|
Loading…
Reference in New Issue
Block a user