Store attributes of plugins in a separate table
This is redesign of plugins architecture in order to store the plugin's attributes in a separate table, not in cluster attributes, so it will be possible to remove connection between plugin and cluster when a plugin gets deleted. Added ability to work with different versions of a plugin. User can choose the preferred version in UI. The test "test_plugin_generator" was removed because no longer relevant. Closes-Bug: #1440046 Implements: blueprint store-plugins-attributes Change-Id: I52115f130bf1c7c80c66e18d0bf9f7acb16dd56c
This commit is contained in:
parent
a5f4c44d08
commit
a25db9a2c6
|
@ -133,7 +133,10 @@ class ClusterAttributesHandler(BaseHandler):
|
|||
if not cluster.attributes:
|
||||
raise self.http(500, "No attributes found!")
|
||||
|
||||
return objects.Cluster.get_editable_attributes(cluster)
|
||||
return {
|
||||
'editable': objects.Cluster.get_editable_attributes(
|
||||
cluster, all_plugins_versions=True)
|
||||
}
|
||||
|
||||
def PUT(self, cluster_id):
|
||||
""":returns: JSONized Cluster attributes.
|
||||
|
@ -170,8 +173,8 @@ class ClusterAttributesHandler(BaseHandler):
|
|||
# we want to change and block an entire operation if there
|
||||
# one with always_editable=False.
|
||||
if cluster.is_locked:
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster)
|
||||
editable = attrs['editable']
|
||||
editable = objects.Cluster.get_editable_attributes(
|
||||
cluster, all_plugins_versions=True)
|
||||
|
||||
for group_name in data.get('editable', {}):
|
||||
# we need bunch of gets because the attribute may not
|
||||
|
@ -183,7 +186,10 @@ class ClusterAttributesHandler(BaseHandler):
|
|||
"after or during deployment.".format(group_name)))
|
||||
|
||||
objects.Cluster.patch_attributes(cluster, data)
|
||||
return objects.Cluster.get_editable_attributes(cluster)
|
||||
return {
|
||||
'editable': objects.Cluster.get_editable_attributes(
|
||||
cluster, all_plugins_versions=True)
|
||||
}
|
||||
|
||||
|
||||
class ClusterAttributesDefaultsHandler(BaseHandler):
|
||||
|
|
|
@ -25,6 +25,7 @@ revision = '43b2cb64dae6'
|
|||
down_revision = '1e50a4903910'
|
||||
|
||||
from alembic import op
|
||||
from nailgun.db.sqlalchemy.models import fields
|
||||
from oslo_serialization import jsonutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql as psql
|
||||
|
@ -106,9 +107,11 @@ def upgrade():
|
|||
task_names_upgrade()
|
||||
add_node_discover_error_upgrade()
|
||||
upgrade_neutron_parameters()
|
||||
upgrade_cluster_plugins()
|
||||
|
||||
|
||||
def downgrade():
|
||||
downgrade_cluster_plugins()
|
||||
downgrade_neutron_parameters()
|
||||
add_node_discover_error_downgrade()
|
||||
task_names_downgrade()
|
||||
|
@ -320,3 +323,154 @@ def upgrade_neutron_parameters():
|
|||
def downgrade_neutron_parameters():
|
||||
op.drop_column('neutron_config', 'floating_name')
|
||||
op.drop_column('neutron_config', 'internal_name')
|
||||
|
||||
|
||||
def upgrade_cluster_plugins():
|
||||
op.alter_column(
|
||||
'cluster_plugins',
|
||||
'cluster_id',
|
||||
nullable=False
|
||||
)
|
||||
op.drop_constraint(
|
||||
'cluster_plugins_cluster_id_fkey',
|
||||
'cluster_plugins',
|
||||
type_='foreignkey'
|
||||
)
|
||||
op.create_foreign_key(
|
||||
'cluster_plugins_cluster_id_fkey',
|
||||
'cluster_plugins', 'clusters',
|
||||
['cluster_id'], ['id'],
|
||||
ondelete='CASCADE'
|
||||
)
|
||||
op.add_column(
|
||||
'cluster_plugins',
|
||||
sa.Column(
|
||||
'enabled',
|
||||
sa.Boolean,
|
||||
nullable=False,
|
||||
server_default='false'
|
||||
)
|
||||
)
|
||||
op.add_column(
|
||||
'cluster_plugins',
|
||||
sa.Column(
|
||||
'attributes',
|
||||
fields.JSON(),
|
||||
nullable=False,
|
||||
server_default='{}'
|
||||
)
|
||||
)
|
||||
|
||||
# Iterate over all editable cluster attributes,
|
||||
# and set entry in 'cluster_plugins' table
|
||||
|
||||
connection = op.get_bind()
|
||||
|
||||
q_get_plugins = sa.text('''
|
||||
SELECT id, name FROM plugins
|
||||
''')
|
||||
q_get_cluster_attributes = sa.text('''
|
||||
SELECT cluster_id, editable FROM attributes
|
||||
''')
|
||||
q_update_cluster_attributes = sa.text('''
|
||||
UPDATE attributes
|
||||
SET editable = :editable
|
||||
WHERE cluster_id = :cluster_id
|
||||
''')
|
||||
q_get_cluster_plugins = sa.text('''
|
||||
SELECT id FROM cluster_plugins
|
||||
WHERE cluster_id = :cluster_id AND plugin_id = :plugin_id
|
||||
''')
|
||||
q_update_cluster_plugins = sa.text('''
|
||||
UPDATE cluster_plugins
|
||||
SET enabled = :enabled, attributes = :attributes
|
||||
WHERE cluster_id = :cluster_id AND plugin_id = :plugin_id
|
||||
''')
|
||||
q_insert_cluster_plugins = sa.text('''
|
||||
INSERT INTO cluster_plugins
|
||||
(cluster_id, plugin_id, enabled, attributes)
|
||||
VALUES
|
||||
(:cluster_id, :plugin_id, :enabled, :attributes)
|
||||
''')
|
||||
|
||||
plugins = list(connection.execute(q_get_plugins))
|
||||
for cluster_id, editable in connection.execute(q_get_cluster_attributes):
|
||||
editable = jsonutils.loads(editable)
|
||||
for plugin_id, plugin_name in plugins:
|
||||
if plugin_name in editable:
|
||||
attributes = editable.pop(plugin_name)
|
||||
metadata = attributes.pop('metadata')
|
||||
|
||||
if connection.execute(q_get_cluster_plugins,
|
||||
cluster_id=cluster_id,
|
||||
plugin_id=plugin_id).first():
|
||||
action = q_update_cluster_plugins
|
||||
else:
|
||||
action = q_insert_cluster_plugins
|
||||
|
||||
connection.execute(
|
||||
action,
|
||||
cluster_id=cluster_id,
|
||||
plugin_id=plugin_id,
|
||||
enabled=metadata['enabled'],
|
||||
attributes=jsonutils.dumps(attributes)
|
||||
)
|
||||
connection.execute(
|
||||
q_update_cluster_attributes,
|
||||
cluster_id=cluster_id,
|
||||
editable=jsonutils.dumps(editable)
|
||||
)
|
||||
|
||||
|
||||
def downgrade_cluster_plugins():
|
||||
connection = op.get_bind()
|
||||
|
||||
q_get_cluster_attributes = sa.text('''
|
||||
SELECT clusters.id, attributes.editable
|
||||
FROM attributes JOIN clusters ON (attributes.cluster_id = clusters.id)
|
||||
''')
|
||||
q_get_plugins = sa.text('''
|
||||
SELECT plugins.id, plugins.name, plugins.title,
|
||||
cluster_plugins.enabled, cluster_plugins.attributes
|
||||
FROM plugins JOIN cluster_plugins
|
||||
ON (plugins.id = cluster_plugins.plugin_id)
|
||||
WHERE cluster_plugins.cluster_id = :cluster_id
|
||||
''')
|
||||
q_update_cluster_attributes = sa.text('''
|
||||
UPDATE attributes
|
||||
SET editable = :editable
|
||||
WHERE cluster_id = :cluster_id
|
||||
''')
|
||||
|
||||
for cluster_id, editable in connection.execute(q_get_cluster_attributes):
|
||||
editable = jsonutils.loads(editable)
|
||||
plugins = connection.execute(q_get_plugins, cluster_id=cluster_id)
|
||||
for p_id, p_name, p_title, p_enabled, p_attr in plugins:
|
||||
p_attr = jsonutils.loads(p_attr)
|
||||
p_attr['metadata'].update({
|
||||
'plugin_id': p_id,
|
||||
'enabled': p_enabled,
|
||||
'label': p_title
|
||||
})
|
||||
editable[p_name] = p_attr
|
||||
connection.execute(q_update_cluster_attributes,
|
||||
cluster_id=cluster_id,
|
||||
editable=jsonutils.dumps(editable))
|
||||
|
||||
op.drop_column('cluster_plugins', 'attributes')
|
||||
op.drop_column('cluster_plugins', 'enabled')
|
||||
op.drop_constraint(
|
||||
'cluster_plugins_cluster_id_fkey',
|
||||
'cluster_plugins',
|
||||
type_='foreignkey'
|
||||
)
|
||||
op.create_foreign_key(
|
||||
'cluster_plugins_cluster_id_fkey',
|
||||
'cluster_plugins', 'clusters',
|
||||
['cluster_id'], ['id']
|
||||
)
|
||||
op.alter_column(
|
||||
'cluster_plugins',
|
||||
'cluster_id',
|
||||
nullable=None
|
||||
)
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy import Boolean
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy.ext.mutable import MutableDict
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import String
|
||||
|
@ -28,10 +30,25 @@ from nailgun.db.sqlalchemy.models.fields import JSON
|
|||
class ClusterPlugins(Base):
|
||||
|
||||
__tablename__ = 'cluster_plugins'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
plugin_id = Column(Integer, ForeignKey('plugins.id', ondelete='CASCADE'),
|
||||
plugin_id = Column(Integer,
|
||||
ForeignKey('plugins.id', ondelete='CASCADE'),
|
||||
nullable=False)
|
||||
cluster_id = Column(Integer, ForeignKey('clusters.id'))
|
||||
cluster_id = Column(Integer,
|
||||
ForeignKey('clusters.id', ondelete='CASCADE'),
|
||||
nullable=False)
|
||||
enabled = Column(Boolean,
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default='false')
|
||||
# Initially, 'attributes' is a copy of 'Plugin.attributes_metadata'.
|
||||
# We need this column in order to store in there the modified (by user)
|
||||
# version of attributes, because we don't want to store them in cluster
|
||||
# attributes with no chance to remove.
|
||||
attributes = Column(MutableDict.as_mutable(JSON),
|
||||
nullable=False,
|
||||
server_default='{}')
|
||||
|
||||
|
||||
class Plugin(Base):
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"title": "The Logging, Monitoring and Alerting (LMA) Collector Plugin",
|
||||
"version": "0.7.0",
|
||||
"description": "Collect logs, metrics and notifications from system and OpenStack services and forward that information to external backends such as Elasticsearch and InfluxDB.",
|
||||
"fuel_version": ["6.1"],
|
||||
"fuel_version": ["7.0"],
|
||||
"authors": ["Mirantis Inc."],
|
||||
"licenses": ["Apache License Version 2.0"],
|
||||
"homepage": "https://github.com/openstack/fuel-plugin-lma-collector",
|
||||
|
@ -33,9 +33,25 @@
|
|||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/centos"
|
||||
},
|
||||
{
|
||||
"os": "ubuntu",
|
||||
"version": "2015.1-8.0",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/ubuntu"
|
||||
}
|
||||
],
|
||||
"package_version": "2.0.0"
|
||||
"package_version": "2.0.0",
|
||||
"attributes_metadata": {
|
||||
"logging_text": {
|
||||
"value": "value",
|
||||
"type": "text",
|
||||
"description": "description",
|
||||
"weight": 25,
|
||||
"label": "label"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -63,6 +79,13 @@
|
|||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/centos"
|
||||
},
|
||||
{
|
||||
"os": "ubuntu",
|
||||
"version": "2015.1-8.0",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/ubuntu"
|
||||
}
|
||||
],
|
||||
"package_version": "2.0.0"
|
||||
|
@ -76,7 +99,7 @@
|
|||
"title": "Zabbix for Fuel",
|
||||
"version": "1.0.0",
|
||||
"description": "Enables Zabbix Monitoring. For information how to access Zabbix UI refer to Zabbix plugin User Guide. Zabbix URL schema is http://<VIP>/zabbix",
|
||||
"fuel_version": ["6.1"],
|
||||
"fuel_version": ["7.0"],
|
||||
"authors": ["Dmitry Klenov <dklenov@mirantis.com>", "Piotr Misiak <pmisiak@mirantis.com>", "Szymon Banka <sbanka@mirantis.com>", "Bartosz Kupidura <bkupidura@mirantis.com>", "Alexander Zatserklyany <azatserklyany@mirantis.com>"],
|
||||
"licenses": ["Apache License Version 2.0"],
|
||||
"homepage": "https://github.com/openstack/fuel-plugin-external-zabbix",
|
||||
|
@ -95,9 +118,80 @@
|
|||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/centos"
|
||||
},
|
||||
{
|
||||
"os": "ubuntu",
|
||||
"version": "2015.1-8.0",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/ubuntu"
|
||||
}
|
||||
],
|
||||
"package_version": "2.0.0"
|
||||
"package_version": "2.0.0",
|
||||
"attributes_metadata": {
|
||||
"zabbix_text_1": {
|
||||
"value": "value 1.1",
|
||||
"type": "text",
|
||||
"description": "description 1.1",
|
||||
"weight": 25,
|
||||
"label": "label 1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 4,
|
||||
"model": "nailgun.plugin",
|
||||
"fields": {
|
||||
"name": "zabbix_monitoring",
|
||||
"title": "Zabbix for Fuel",
|
||||
"version": "2.0.0",
|
||||
"description": "Enables Zabbix Monitoring. For information how to access Zabbix UI refer to Zabbix plugin User Guide. Zabbix URL schema is http://<VIP>/zabbix",
|
||||
"fuel_version": ["7.0"],
|
||||
"authors": ["Dmitry Klenov <dklenov@mirantis.com>", "Piotr Misiak <pmisiak@mirantis.com>", "Szymon Banka <sbanka@mirantis.com>", "Bartosz Kupidura <bkupidura@mirantis.com>", "Alexander Zatserklyany <azatserklyany@mirantis.com>"],
|
||||
"licenses": ["Apache License Version 2.0"],
|
||||
"homepage": "https://github.com/openstack/fuel-plugin-external-zabbix",
|
||||
"groups": ["monitoring"],
|
||||
"releases": [
|
||||
{
|
||||
"os": "ubuntu",
|
||||
"version": "2014.2-6.0",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/ubuntu"
|
||||
},
|
||||
{
|
||||
"os": "centos",
|
||||
"version": "2014.2-6.1",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/centos"
|
||||
},
|
||||
{
|
||||
"os": "ubuntu",
|
||||
"version": "2015.1-8.0",
|
||||
"mode": ["ha"],
|
||||
"deployment_scripts_path": "deployment_scripts/",
|
||||
"repository_path": "repositories/ubuntu"
|
||||
}
|
||||
],
|
||||
"package_version": "2.0.0",
|
||||
"attributes_metadata": {
|
||||
"zabbix_text_1": {
|
||||
"value": "value 2.1",
|
||||
"type": "text",
|
||||
"description": "description 2.1",
|
||||
"weight": 25,
|
||||
"label": "label 2.1"
|
||||
},
|
||||
"zabbix_text_2": {
|
||||
"value": "value 2.2",
|
||||
"type": "text",
|
||||
"description": "description 2.2",
|
||||
"weight": 26,
|
||||
"label": "label 2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -50,6 +50,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.network_group import NetworkGroup
|
||||
from nailgun.objects.network_group import NetworkGroupCollection
|
||||
|
|
|
@ -24,7 +24,8 @@ from distutils.version import StrictVersion
|
|||
import six
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql as psql
|
||||
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
|
||||
from nailgun import consts
|
||||
from nailgun.db import db
|
||||
|
@ -35,6 +36,7 @@ from nailgun.extensions import fire_callback_on_node_collection_delete
|
|||
from nailgun.logger import logger
|
||||
from nailgun.objects import NailgunCollection
|
||||
from nailgun.objects import NailgunObject
|
||||
from nailgun.objects.plugin import ClusterPlugins
|
||||
from nailgun.objects import Release
|
||||
from nailgun.objects.serializers.cluster import ClusterSerializer
|
||||
from nailgun.plugins.manager import PluginManager
|
||||
|
@ -166,7 +168,6 @@ class Cluster(NailgunObject):
|
|||
|
||||
if assign_nodes:
|
||||
cls.update_nodes(new_cluster, assign_nodes)
|
||||
|
||||
except (
|
||||
errors.OutOfVLANs,
|
||||
errors.OutOfIPs,
|
||||
|
@ -177,6 +178,8 @@ class Cluster(NailgunObject):
|
|||
|
||||
db().flush()
|
||||
|
||||
ClusterPlugins.add_compatible_plugins(new_cluster)
|
||||
|
||||
return new_cluster
|
||||
|
||||
@classmethod
|
||||
|
@ -235,31 +238,65 @@ class Cluster(NailgunObject):
|
|||
:returns: Dict object
|
||||
"""
|
||||
editable = instance.release.attributes_metadata.get("editable")
|
||||
# when attributes created we need to understand whether should plugin
|
||||
# be applied for created cluster
|
||||
plugin_attrs = PluginManager.get_plugin_attributes(instance)
|
||||
# Add default attributes of connected plugins
|
||||
plugin_attrs = PluginManager.get_plugins_attributes(
|
||||
instance, all_versions=True, default=True)
|
||||
editable = dict(plugin_attrs, **editable)
|
||||
editable = traverse(editable, AttributesGenerator, {
|
||||
'cluster': instance,
|
||||
'settings': settings,
|
||||
})
|
||||
|
||||
return editable
|
||||
|
||||
@classmethod
|
||||
def get_attributes(cls, instance):
|
||||
"""Get attributes for current Cluster instance
|
||||
def get_attributes(cls, instance, all_plugins_versions=False):
|
||||
"""Get attributes for current Cluster instance.
|
||||
|
||||
:param instance: Cluster instance
|
||||
:returns: Attributes instance
|
||||
:param all_plugins_versions: Get attributes of all versions of plugins
|
||||
:returns: dict
|
||||
"""
|
||||
return db().query(models.Attributes).filter(
|
||||
models.Attributes.cluster_id == instance.id
|
||||
).first()
|
||||
try:
|
||||
attrs = db().query(models.Attributes).filter(
|
||||
models.Attributes.cluster_id == instance.id
|
||||
).one()
|
||||
except MultipleResultsFound:
|
||||
raise errors.InvalidData(
|
||||
u"Multiple rows with attributes were found for cluster '{0}'"
|
||||
.format(instance.name)
|
||||
)
|
||||
except NoResultFound:
|
||||
raise errors.InvalidData(
|
||||
u"No attributes were found for cluster '{0}'"
|
||||
.format(instance.name)
|
||||
)
|
||||
attrs = dict(attrs)
|
||||
|
||||
# Merge plugins attributes into editable ones
|
||||
plugin_attrs = PluginManager.get_plugins_attributes(
|
||||
instance, all_versions=all_plugins_versions)
|
||||
plugin_attrs = traverse(plugin_attrs, AttributesGenerator, {
|
||||
'cluster': instance,
|
||||
'settings': settings,
|
||||
})
|
||||
attrs['editable'].update(plugin_attrs)
|
||||
|
||||
return attrs
|
||||
|
||||
@classmethod
|
||||
def get_editable_attributes(cls, instance, all_plugins_versions=False):
|
||||
"""Get editable attributes for current Cluster instance.
|
||||
|
||||
:param instance: Cluster instance
|
||||
:param all_plugins_versions: Get attributes of all versions of plugins
|
||||
:return: dict
|
||||
"""
|
||||
return cls.get_attributes(instance, all_plugins_versions)['editable']
|
||||
|
||||
@classmethod
|
||||
def update_attributes(cls, instance, data):
|
||||
PluginManager.process_cluster_attributes(instance, data['editable'])
|
||||
|
||||
for key, value in data.iteritems():
|
||||
setattr(instance.attributes, key, value)
|
||||
cls.add_pending_changes(instance, "attributes")
|
||||
|
@ -274,23 +311,16 @@ class Cluster(NailgunObject):
|
|||
cls.get_network_manager(instance).update_restricted_networks(instance)
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def get_editable_attributes(cls, instance):
|
||||
attrs = cls.get_attributes(instance)
|
||||
editable = attrs.editable
|
||||
|
||||
return {'editable': editable}
|
||||
|
||||
@classmethod
|
||||
def get_updated_editable_attributes(cls, instance, data):
|
||||
"""Same as get_editable_attributes but also merges given data.
|
||||
|
||||
:param instance: Cluster object
|
||||
:param data: dict
|
||||
:return: dict
|
||||
:returns: dict
|
||||
"""
|
||||
return {'editable': dict_merge(
|
||||
cls.get_editable_attributes(instance)['editable'],
|
||||
cls.get_editable_attributes(instance),
|
||||
data.get('editable', {})
|
||||
)}
|
||||
|
||||
|
@ -936,7 +966,7 @@ class Cluster(NailgunObject):
|
|||
@classmethod
|
||||
def is_vmware_enabled(cls, instance):
|
||||
"""Check if current cluster supports vmware configuration."""
|
||||
attributes = cls.get_attributes(instance).editable
|
||||
attributes = cls.get_editable_attributes(instance)
|
||||
return attributes.get('common', {}).get('use_vcenter', {}).get('value')
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -14,27 +14,48 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from itertools import groupby
|
||||
|
||||
from nailgun.db import db
|
||||
from nailgun.db.sqlalchemy.models import plugins as plugin_db_model
|
||||
from nailgun.objects import base
|
||||
from nailgun.objects.serializers import plugin
|
||||
from nailgun.db.sqlalchemy import models
|
||||
from nailgun.objects import NailgunCollection
|
||||
from nailgun.objects import NailgunObject
|
||||
from nailgun.objects.serializers.plugin import PluginSerializer
|
||||
|
||||
|
||||
class Plugin(base.NailgunObject):
|
||||
class Plugin(NailgunObject):
|
||||
|
||||
model = plugin_db_model.Plugin
|
||||
serializer = plugin.PluginSerializer
|
||||
model = models.Plugin
|
||||
serializer = PluginSerializer
|
||||
|
||||
@classmethod
|
||||
def create(cls, data):
|
||||
new_plugin = super(Plugin, cls).create(data)
|
||||
|
||||
# FIXME (vmygal): This is very ugly hack and it must be fixed ASAP.
|
||||
# Need to remove the syncing of plugin metadata from here.
|
||||
# All plugin metadata must be sent via 'data' argument of this
|
||||
# function and it must be fixed in 'python-fuelclient' repository.
|
||||
from nailgun.plugins.adapters import wrap_plugin
|
||||
plugin_adapter = wrap_plugin(new_plugin)
|
||||
plugin_adapter.sync_metadata_to_db()
|
||||
|
||||
ClusterPlugins.add_compatible_clusters(new_plugin)
|
||||
|
||||
return new_plugin
|
||||
|
||||
@classmethod
|
||||
def get_by_name_version(cls, name, version):
|
||||
return db().query(cls.model).\
|
||||
filter_by(name=name, version=version).first()
|
||||
return db()\
|
||||
.query(cls.model)\
|
||||
.filter_by(name=name, version=version)\
|
||||
.first()
|
||||
|
||||
|
||||
class PluginCollection(base.NailgunCollection):
|
||||
class PluginCollection(NailgunCollection):
|
||||
|
||||
single = Plugin
|
||||
|
||||
|
@ -68,12 +89,190 @@ class PluginCollection(base.NailgunCollection):
|
|||
|
||||
@classmethod
|
||||
def get_by_uids(cls, plugin_ids):
|
||||
"""Returns plugins by given ids.
|
||||
"""Returns plugins by given IDs.
|
||||
|
||||
:param plugin_ids: list of plugin ids
|
||||
:param plugin_ids: list of plugin IDs
|
||||
:type plugin_ids: list
|
||||
|
||||
:returns: iterable (SQLAlchemy query)
|
||||
"""
|
||||
return cls.filter_by_id_list(
|
||||
cls.all(), plugin_ids)
|
||||
return cls.filter_by_id_list(cls.all(), plugin_ids)
|
||||
|
||||
|
||||
class ClusterPlugins(NailgunObject):
|
||||
|
||||
model = models.ClusterPlugins
|
||||
|
||||
@classmethod
|
||||
def validate_compatibility(cls, cluster, plugin):
|
||||
"""Validates if plugin is compatible with cluster.
|
||||
|
||||
- validates operating systems
|
||||
- modes of clusters (simple or ha)
|
||||
- release version
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.objects.cluster.Cluster
|
||||
:param plugin: A plugin instance
|
||||
:type plugin: nailgun.objects.plugin.Plugin
|
||||
:return: True if compatible, False if not
|
||||
:rtype: bool
|
||||
"""
|
||||
for release in plugin.releases:
|
||||
os_compat = cluster.release.operating_system.lower()\
|
||||
== release['os'].lower()
|
||||
# plugin writer should be able to specify ha in release['mode']
|
||||
# and know nothing about ha_compact
|
||||
mode_compat = any(mode in cluster.mode for mode in release['mode'])
|
||||
release_version_compat = cls.is_release_version_compatible(
|
||||
cluster.release.version, release['version'])
|
||||
if all((os_compat, mode_compat, release_version_compat)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_release_version_compatible(rel_version, plugin_rel_version):
|
||||
"""Checks if release version is compatible with plugin version.
|
||||
|
||||
:param rel_version: Release version
|
||||
:type rel_version: str
|
||||
:param plugin_rel_version: Plugin release version
|
||||
:type plugin_rel_version: str
|
||||
:return: True if compatible, False if not
|
||||
:rtype: bool
|
||||
"""
|
||||
rel_os, rel_fuel = rel_version.split('-')
|
||||
plugin_os, plugin_rel = plugin_rel_version.split('-')
|
||||
|
||||
return rel_os.startswith(plugin_os) and rel_fuel.startswith(plugin_rel)
|
||||
|
||||
@classmethod
|
||||
def get_compatible_plugins(cls, cluster):
|
||||
"""Returns a list of plugins that are compatible with a given cluster.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.objects.cluster.Cluster
|
||||
:return: A list of plugin instances
|
||||
:rtype: list
|
||||
"""
|
||||
return list(six.moves.filter(
|
||||
lambda p: cls.validate_compatibility(cluster, p),
|
||||
PluginCollection.all()))
|
||||
|
||||
@classmethod
|
||||
def add_compatible_plugins(cls, cluster):
|
||||
"""Populates 'cluster_plugins' table with compatible plugins.
|
||||
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.objects.cluster.Cluster
|
||||
"""
|
||||
for plugin in cls.get_compatible_plugins(cluster):
|
||||
cls.create({
|
||||
'cluster_id': cluster.id,
|
||||
'plugin_id': plugin.id,
|
||||
'enabled': False,
|
||||
'attributes': plugin.attributes_metadata
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def get_compatible_clusters(cls, plugin):
|
||||
"""Returns a list of clusters that are compatible with a given plugin.
|
||||
|
||||
:param plugin: A plugin instance
|
||||
:type plugin: nailgun.objects.plugin.Plugin
|
||||
:return: A list of cluster instances
|
||||
:rtype: list
|
||||
"""
|
||||
return list(six.moves.filter(
|
||||
lambda c: cls.validate_compatibility(c, plugin),
|
||||
db().query(models.Cluster)))
|
||||
|
||||
@classmethod
|
||||
def add_compatible_clusters(cls, plugin):
|
||||
"""Populates 'cluster_plugins' table with compatible cluster.
|
||||
|
||||
:param plugin: A plugin instance
|
||||
:type plugin: nailgun.objects.plugin.Plugin
|
||||
"""
|
||||
for cluster in cls.get_compatible_clusters(plugin):
|
||||
cls.create({
|
||||
'cluster_id': cluster.id,
|
||||
'plugin_id': plugin.id,
|
||||
'enabled': False,
|
||||
'attributes': plugin.attributes_metadata
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def set_attributes(cls, cluster_id, plugin_id, enabled=None, attrs=None):
|
||||
"""Sets plugin's attributes in cluster_plugins table.
|
||||
|
||||
:param cluster_id: Cluster ID
|
||||
:type cluster_id: int
|
||||
:param plugin_id: Plugin ID
|
||||
:type plugin_id: int
|
||||
:param enabled: Enabled or disabled plugin for given cluster
|
||||
:type enabled: bool
|
||||
:param attrs: Plugin metadata
|
||||
:type attrs: dict
|
||||
"""
|
||||
params = {}
|
||||
if enabled is not None:
|
||||
params['enabled'] = enabled
|
||||
if attrs is not None:
|
||||
params['attributes'] = attrs
|
||||
|
||||
db().query(cls.model)\
|
||||
.filter_by(plugin_id=plugin_id, cluster_id=cluster_id)\
|
||||
.update(params, synchronize_session='fetch')
|
||||
db().flush()
|
||||
|
||||
@classmethod
|
||||
def get_connected_plugins(cls, cluster_id):
|
||||
"""Returns plugins connected with given cluster.
|
||||
|
||||
:param cluster_id: Cluster ID
|
||||
:type cluster_id: int
|
||||
:returns: List of plugins
|
||||
:rtype: iterable (SQLAlchemy query)
|
||||
"""
|
||||
return db().query(
|
||||
models.Plugin.id,
|
||||
models.Plugin.name,
|
||||
models.Plugin.title,
|
||||
models.Plugin.version,
|
||||
cls.model.enabled,
|
||||
models.Plugin.attributes_metadata,
|
||||
cls.model.attributes
|
||||
).join(cls.model)\
|
||||
.filter(cls.model.cluster_id == cluster_id)\
|
||||
.order_by(models.Plugin.name)\
|
||||
.order_by(models.Plugin.version)
|
||||
|
||||
@classmethod
|
||||
def get_connected_clusters(cls, plugin_id):
|
||||
"""Returns clusters connected with given plugin.
|
||||
|
||||
:param plugin_id: Plugin ID
|
||||
:type plugin_id: int
|
||||
:returns: List of clusters
|
||||
:rtype: iterable (SQLAlchemy query)
|
||||
"""
|
||||
return db()\
|
||||
.query(models.Cluster)\
|
||||
.join(cls.model)\
|
||||
.filter(cls.model.plugin_id == plugin_id)\
|
||||
.order_by(models.Cluster.name)
|
||||
|
||||
@classmethod
|
||||
def get_enabled(cls, cluster_id):
|
||||
"""Returns a list of plugins enabled for a given cluster.
|
||||
|
||||
:param cluster_id: Cluster ID
|
||||
:type cluster_id: int
|
||||
:returns: List of plugin instances
|
||||
:rtype: iterable (SQLAlchemy query)
|
||||
"""
|
||||
return db().query(models.Plugin)\
|
||||
.join(cls.model)\
|
||||
.filter(cls.model.cluster_id == cluster_id)\
|
||||
.filter(cls.model.enabled.is_(True))\
|
||||
.all()
|
||||
|
|
|
@ -74,13 +74,13 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
|||
enabled.
|
||||
"""
|
||||
# Get Mellanox data
|
||||
neutron_mellanox_data = \
|
||||
Cluster.get_attributes(cluster).editable\
|
||||
neutron_mellanox_data = \
|
||||
Cluster.get_editable_attributes(cluster)\
|
||||
.get('neutron_mellanox', {})
|
||||
|
||||
# Get storage data
|
||||
storage_data = \
|
||||
Cluster.get_attributes(cluster).editable.get('storage', {})
|
||||
Cluster.get_editable_attributes(cluster).get('storage', {})
|
||||
|
||||
# Get network manager
|
||||
nm = Cluster.get_network_manager(cluster)
|
||||
|
@ -192,7 +192,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
|||
if cluster.release.operating_system == 'RHEL':
|
||||
attrs['amqp'] = {'provider': 'qpid-rh'}
|
||||
|
||||
cluster_attrs = Cluster.get_attributes(cluster).editable
|
||||
cluster_attrs = Cluster.get_editable_attributes(cluster)
|
||||
if 'nsx_plugin' in cluster_attrs and \
|
||||
cluster_attrs['nsx_plugin']['metadata']['enabled']:
|
||||
attrs['L2']['provider'] = 'nsx'
|
||||
|
@ -500,7 +500,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
|||
}
|
||||
|
||||
# Set non-default ml2 configurations
|
||||
attrs = Cluster.get_attributes(cluster).editable
|
||||
attrs = Cluster.get_editable_attributes(cluster)
|
||||
if 'neutron_mellanox' in attrs and \
|
||||
attrs['neutron_mellanox']['plugin']['value'] == 'ethernet':
|
||||
res['mechanism_drivers'] = 'mlnx,openvswitch'
|
||||
|
@ -515,7 +515,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
|||
l3 = {
|
||||
"use_namespaces": True
|
||||
}
|
||||
attrs = Cluster.get_attributes(cluster).editable
|
||||
attrs = Cluster.get_editable_attributes(cluster)
|
||||
if 'nsx_plugin' in attrs and \
|
||||
attrs['nsx_plugin']['metadata']['enabled']:
|
||||
dhcp_attrs = l3.setdefault('dhcp_agent', {})
|
||||
|
|
|
@ -109,7 +109,8 @@ class ExpressionBasedTask(DeploymentHook):
|
|||
@property
|
||||
def _expression_context(self):
|
||||
return {'cluster': self.cluster,
|
||||
'settings': self.cluster.attributes.editable}
|
||||
'settings':
|
||||
objects.Cluster.get_editable_attributes(self.cluster)}
|
||||
|
||||
def should_execute(self):
|
||||
if 'condition' not in self.task:
|
||||
|
@ -259,7 +260,7 @@ class GenerateHaproxyKeys(GenericRolesHook):
|
|||
uids = self.get_uids()
|
||||
self.task['parameters']['cmd'] = self.task['parameters']['cmd'].format(
|
||||
CLUSTER_ID=self.cluster.id,
|
||||
CN_HOSTNAME=self.cluster.attributes.editable
|
||||
CN_HOSTNAME=objects.Cluster.get_editable_attributes(self.cluster)
|
||||
['public_ssl']['hostname']['value'])
|
||||
yield templates.make_shell_task(uids, self.task)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import yaml
|
|||
from nailgun.errors import errors
|
||||
from nailgun.logger import logger
|
||||
from nailgun.objects.component import Component
|
||||
from nailgun.objects.plugin import ClusterPlugins
|
||||
from nailgun.objects.plugin import Plugin
|
||||
from nailgun.settings import settings
|
||||
|
||||
|
@ -49,9 +50,6 @@ class PluginAdapterBase(object):
|
|||
self.plugin_path = os.path.join(
|
||||
settings.PLUGINS_PATH,
|
||||
self.path_name)
|
||||
self.config_file = os.path.join(
|
||||
self.plugin_path,
|
||||
self.environment_config_name)
|
||||
self.tasks = []
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -92,83 +90,13 @@ class PluginAdapterBase(object):
|
|||
item['role'].append('primary-controller')
|
||||
return data
|
||||
|
||||
def get_plugin_attributes(self, cluster):
|
||||
"""Should be used for initial configuration uploading to custom storage
|
||||
|
||||
Will be invoked in 2 cases:
|
||||
1. Cluster is created but there was no plugins in system
|
||||
on that time, so when plugin is uploaded we need to iterate
|
||||
over all clusters and decide if plugin should be applied
|
||||
2. Plugins is uploaded before cluster creation, in this case
|
||||
we will iterate over all plugins and upload configuration for them
|
||||
|
||||
In this case attributes will be added to same cluster attributes
|
||||
model and stored in editable field
|
||||
"""
|
||||
config = {}
|
||||
if os.path.exists(self.config_file):
|
||||
config = self._load_config(self.config_file)
|
||||
if self.validate_cluster_compatibility(cluster):
|
||||
attrs = config.get("attributes", {})
|
||||
self.update_metadata(attrs)
|
||||
return {self.plugin.name: attrs}
|
||||
return {}
|
||||
|
||||
def validate_cluster_compatibility(self, cluster):
|
||||
"""Validates if plugin is compatible with cluster
|
||||
|
||||
- validates operating systems
|
||||
- modes of clusters (simple or ha)
|
||||
- release version
|
||||
"""
|
||||
for release in self.plugin.releases:
|
||||
os_compat = (cluster.release.operating_system.lower()
|
||||
== release['os'].lower())
|
||||
# plugin writer should be able to specify ha in release['mode']
|
||||
# and know nothing about ha_compact
|
||||
mode_compat = any(mode in cluster.mode for mode in release['mode'])
|
||||
release_version_compat = self._is_release_version_compatible(
|
||||
cluster.release.version, release['version'])
|
||||
if all((os_compat, mode_compat, release_version_compat)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_release_version_compatible(self, rel_version, plugin_rel_version):
|
||||
"""Checks if release version is compatible with plugin version
|
||||
|
||||
:param str rel_version: release version
|
||||
:param str plugin_rel_version: plugin release version
|
||||
:returns: True if compatible, Fals if not
|
||||
"""
|
||||
rel_os, rel_fuel = rel_version.split('-')
|
||||
plugin_os, plugin_rel = plugin_rel_version.split('-')
|
||||
|
||||
return rel_os.startswith(plugin_os) and rel_fuel.startswith(plugin_rel)
|
||||
|
||||
def update_metadata(self, attributes):
|
||||
"""Overwrites only default values in metadata
|
||||
|
||||
Plugin should be able to provide UI "native" conditions
|
||||
to enable/disable plugin on UI itself
|
||||
"""
|
||||
attributes.setdefault('metadata', {})
|
||||
attributes['metadata'].update(self.default_metadata)
|
||||
return attributes
|
||||
|
||||
@property
|
||||
def default_metadata(self):
|
||||
return {u'enabled': False, u'toggleable': True,
|
||||
u'weight': 70, u'label': self.plugin.title,
|
||||
'plugin_id': self.plugin.id}
|
||||
|
||||
def set_cluster_tasks(self):
|
||||
"""Load plugins provided tasks and set them to instance tasks variable
|
||||
|
||||
Provided tasks are loaded from tasks config file.
|
||||
"""
|
||||
task_yaml = os.path.join(
|
||||
self.plugin_path,
|
||||
self.task_config_name)
|
||||
self.plugin_path, self.task_config_name)
|
||||
if os.path.exists(task_yaml):
|
||||
self.tasks = self._load_tasks(task_yaml)
|
||||
|
||||
|
@ -222,13 +150,14 @@ class PluginAdapterBase(object):
|
|||
|
||||
def get_release_info(self, release):
|
||||
"""Get plugin release information which corresponds to given release"""
|
||||
os = release.operating_system.lower()
|
||||
rel_os = release.operating_system.lower()
|
||||
version = release.version
|
||||
|
||||
release_info = filter(
|
||||
lambda r: (
|
||||
r['os'] == os and
|
||||
self._is_release_version_compatible(version, r['version'])),
|
||||
r['os'] == rel_os and
|
||||
ClusterPlugins.is_release_version_compatible(version,
|
||||
r['version'])),
|
||||
self.plugin.releases)
|
||||
|
||||
return release_info[0]
|
||||
|
@ -334,6 +263,8 @@ class PluginAdapterV3(PluginAdapterV2):
|
|||
# Plugin columns have constraints for nullable data, so
|
||||
# we need to check it
|
||||
if attribute_data:
|
||||
if attribute == 'attributes_metadata':
|
||||
attribute_data = attribute_data['attributes']
|
||||
data_to_update[attribute] = attribute_data
|
||||
|
||||
Plugin.update(self.plugin, data_to_update)
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import six
|
||||
from six.moves import map
|
||||
|
||||
from nailgun.errors import errors
|
||||
from nailgun.logger import logger
|
||||
from nailgun.objects.plugin import ClusterPlugins
|
||||
from nailgun.objects.plugin import Plugin
|
||||
from nailgun.objects.plugin import PluginCollection
|
||||
from nailgun.plugins.adapters import wrap_plugin
|
||||
|
@ -25,60 +27,135 @@ from nailgun.plugins.adapters import wrap_plugin
|
|||
class PluginManager(object):
|
||||
|
||||
@classmethod
|
||||
def process_cluster_attributes(cls, cluster, attrs):
|
||||
def process_cluster_attributes(cls, cluster, attributes):
|
||||
"""Generate Cluster-Plugins relation based on attributes
|
||||
|
||||
Iterates through plugins attributes, creates
|
||||
or deletes Cluster <-> Plugins relation if plugin
|
||||
is enabled or disabled.
|
||||
|
||||
:param cluster: Cluster object
|
||||
:param attrs: dictionary with cluster attributes
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.objects.cluster.Cluster
|
||||
:param attributes: Cluster attributes
|
||||
:type attributes: dict
|
||||
"""
|
||||
for key, attr in six.iteritems(attrs):
|
||||
cls._process_attr(cluster, attr)
|
||||
def _convert_attrs(plugin_id, attrs):
|
||||
prefix = "#{0}_".format(plugin_id)
|
||||
result = dict((title[len(prefix):], attrs[title])
|
||||
for title in attrs
|
||||
if title.startswith(prefix))
|
||||
for attr in six.itervalues(result):
|
||||
if 'restrictions' not in attr:
|
||||
continue
|
||||
if len(attr['restrictions']) == 1:
|
||||
attr.pop('restrictions')
|
||||
else:
|
||||
attr['restrictions'].pop()
|
||||
return result
|
||||
|
||||
for attrs in six.itervalues(attributes):
|
||||
if not isinstance(attrs, dict):
|
||||
continue
|
||||
|
||||
plugin_versions = attrs.pop('plugin_versions', None)
|
||||
if plugin_versions is None:
|
||||
continue
|
||||
|
||||
metadata = attrs.pop('metadata', {})
|
||||
plugin_enabled = metadata.get('enabled', False)
|
||||
default = metadata.get('default', False)
|
||||
|
||||
for version in plugin_versions['values']:
|
||||
pid = version.get('data')
|
||||
plugin = Plugin.get_by_uid(pid)
|
||||
if not plugin:
|
||||
logger.warning(
|
||||
'Plugin with id "%s" is not found, skip it', pid)
|
||||
continue
|
||||
|
||||
enabled = plugin_enabled and\
|
||||
str(plugin.id) == plugin_versions['value']
|
||||
|
||||
ClusterPlugins.set_attributes(
|
||||
cluster.id, plugin.id, enabled=enabled,
|
||||
attrs=_convert_attrs(plugin.id, attrs)
|
||||
if enabled or default else None
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _process_attr(cls, cluster, attr):
|
||||
if not isinstance(attr, dict):
|
||||
return
|
||||
def get_plugins_attributes(
|
||||
cls, cluster, all_versions=False, default=False):
|
||||
"""Gets attributes of all plugins connected with given cluster.
|
||||
|
||||
metadata = attr.get('metadata', {})
|
||||
plugin_id = metadata.get('plugin_id')
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.objects.cluster.Cluster
|
||||
:param all_versions: True to get attributes of all versions of plugins
|
||||
:type all_versions: bool
|
||||
:param default: True to return a default plugins attributes (for UI)
|
||||
:type default: bool
|
||||
:return: Plugins attributes
|
||||
:rtype: dict
|
||||
"""
|
||||
versions = {
|
||||
u'type': u'radio',
|
||||
u'values': [],
|
||||
u'weight': 10,
|
||||
u'value': None,
|
||||
u'label': 'Choose a plugin version'
|
||||
}
|
||||
|
||||
if not plugin_id:
|
||||
return
|
||||
def _convert_attr(pid, name, title, attr):
|
||||
restrictions = attr.setdefault('restrictions', [])
|
||||
restrictions.append({
|
||||
'action': 'hide',
|
||||
'condition': "settings:{0}.plugin_versions.value != '{1}'"
|
||||
.format(name, pid)
|
||||
})
|
||||
return "#{0}_{1}".format(pid, title), attr
|
||||
|
||||
plugin = Plugin.get_by_uid(plugin_id)
|
||||
if not plugin:
|
||||
logger.warning('Plugin with id "%s" is not found, skip it',
|
||||
plugin_id)
|
||||
return
|
||||
plugins_attributes = {}
|
||||
for pid, name, title, version, enabled, default_attrs, cluster_attrs\
|
||||
in ClusterPlugins.get_connected_plugins(cluster.id):
|
||||
if all_versions:
|
||||
enabled = enabled and not default
|
||||
data = plugins_attributes.get(name, {})
|
||||
metadata = data.setdefault('metadata', {
|
||||
u'toggleable': True,
|
||||
u'weight': 70
|
||||
})
|
||||
metadata['enabled'] = enabled or metadata.get('enabled', False)
|
||||
metadata['label'] = title
|
||||
|
||||
enabled = metadata.get('enabled', False)
|
||||
if all_versions:
|
||||
metadata['default'] = default
|
||||
|
||||
# Value is true and plugin is not enabled for this cluster
|
||||
# that means plugin was enabled on this request
|
||||
if enabled and cluster not in plugin.clusters:
|
||||
plugin.clusters.append(cluster)
|
||||
# Value is false and plugin is enabled for this cluster
|
||||
# that means plugin was disabled on this request
|
||||
elif not enabled and cluster in plugin.clusters:
|
||||
plugin.clusters.remove(cluster)
|
||||
attrs = default_attrs if default else cluster_attrs
|
||||
data.update(_convert_attr(pid, name, key, attrs[key])
|
||||
for key in attrs)
|
||||
|
||||
@classmethod
|
||||
def get_plugin_attributes(cls, cluster):
|
||||
plugin_attributes = {}
|
||||
for plugin_db in PluginCollection.all_newest():
|
||||
plugin_adapter = wrap_plugin(plugin_db)
|
||||
attributes = plugin_adapter.get_plugin_attributes(cluster)
|
||||
plugin_attributes.update(attributes)
|
||||
return plugin_attributes
|
||||
if 'plugin_versions' in data:
|
||||
plugin_versions = data['plugin_versions']
|
||||
else:
|
||||
plugin_versions = copy.deepcopy(versions)
|
||||
plugin_versions['values'].append({
|
||||
u'data': str(pid),
|
||||
u'description': '',
|
||||
u'label': version
|
||||
})
|
||||
if not plugin_versions['value'] or enabled:
|
||||
plugin_versions['value'] = str(pid)
|
||||
|
||||
data['plugin_versions'] = plugin_versions
|
||||
else:
|
||||
data.update(cluster_attrs if enabled else {})
|
||||
plugins_attributes[name] = data
|
||||
|
||||
return plugins_attributes
|
||||
|
||||
@classmethod
|
||||
def get_cluster_plugins_with_tasks(cls, cluster):
|
||||
cluster_plugins = []
|
||||
for plugin_db in cluster.plugins:
|
||||
for plugin_db in ClusterPlugins.get_enabled(cluster.id):
|
||||
plugin_adapter = wrap_plugin(plugin_db)
|
||||
plugin_adapter.set_cluster_tasks()
|
||||
cluster_plugins.append(plugin_adapter)
|
||||
|
@ -96,7 +173,7 @@ class PluginManager(object):
|
|||
all_roles = dict((role['id'], role) for role in instance_roles)
|
||||
conflict_roles = dict()
|
||||
|
||||
for plugin in cluster.plugins:
|
||||
for plugin in ClusterPlugins.get_enabled(cluster.id):
|
||||
for role in plugin.network_roles_metadata:
|
||||
role_id = role['id']
|
||||
if role_id in all_roles:
|
||||
|
@ -124,9 +201,10 @@ class PluginManager(object):
|
|||
@classmethod
|
||||
def get_plugins_deployment_tasks(cls, cluster):
|
||||
deployment_tasks = []
|
||||
|
||||
processed_tasks = {}
|
||||
for plugin_adapter in map(wrap_plugin, cluster.plugins):
|
||||
|
||||
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
|
||||
for plugin_adapter in map(wrap_plugin, enabled_plugins):
|
||||
depl_tasks = plugin_adapter.deployment_tasks
|
||||
|
||||
for t in depl_tasks:
|
||||
|
@ -140,7 +218,6 @@ class PluginManager(object):
|
|||
processed_tasks[t_id],
|
||||
t_id)
|
||||
)
|
||||
|
||||
processed_tasks[t_id] = plugin_adapter.full_name
|
||||
|
||||
deployment_tasks.extend(depl_tasks)
|
||||
|
@ -152,7 +229,7 @@ class PluginManager(object):
|
|||
result = {}
|
||||
core_roles = set(cluster.release.roles_metadata)
|
||||
|
||||
for plugin_db in cluster.plugins:
|
||||
for plugin_db in ClusterPlugins.get_enabled(cluster.id):
|
||||
plugin_roles = wrap_plugin(plugin_db).normalized_roles_metadata
|
||||
|
||||
# we should check all possible cases of roles intersection
|
||||
|
@ -181,10 +258,12 @@ class PluginManager(object):
|
|||
|
||||
@classmethod
|
||||
def get_volumes_metadata(cls, cluster):
|
||||
"""Get volumes metadata for cluster from all plugins which enabled it
|
||||
"""Get volumes metadata for cluster from all plugins which enabled it.
|
||||
|
||||
:param cluster: Cluster DB model
|
||||
:returns: dict -- object with merged volumes data from plugins
|
||||
:param cluster: A cluster instance
|
||||
:type cluster: nailgun.db.sqlalchemy.models.cluster.Cluster
|
||||
:return: Object with merged volumes data from plugins
|
||||
:rtype: dict
|
||||
"""
|
||||
volumes_metadata = {
|
||||
'volumes': [],
|
||||
|
@ -194,7 +273,8 @@ class PluginManager(object):
|
|||
release_volumes_ids = [v['id'] for v in release_volumes]
|
||||
processed_volumes = {}
|
||||
|
||||
for plugin_adapter in map(wrap_plugin, cluster.plugins):
|
||||
enabled_plugins = ClusterPlugins.get_enabled(cluster.id)
|
||||
for plugin_adapter in map(wrap_plugin, enabled_plugins):
|
||||
metadata = plugin_adapter.volumes_metadata
|
||||
|
||||
for volume in metadata.get('volumes', []):
|
||||
|
@ -223,9 +303,12 @@ class PluginManager(object):
|
|||
|
||||
@classmethod
|
||||
def sync_plugins_metadata(cls, plugin_ids=None):
|
||||
"""Sync metadata for plugins by given ids.
|
||||
"""Sync metadata for plugins by given IDs.
|
||||
|
||||
If there are no ids all newest plugins will be synced
|
||||
If there are no IDs, all newest plugins will be synced.
|
||||
|
||||
:param plugin_ids: list of plugin IDs
|
||||
:type plugin_ids: list
|
||||
"""
|
||||
if plugin_ids:
|
||||
plugins = PluginCollection.get_by_uids(plugin_ids)
|
||||
|
|
|
@ -39,6 +39,7 @@ from nailgun.db.sqlalchemy.models import Release
|
|||
from nailgun.logger import logger
|
||||
from nailgun.network import connectivity_check
|
||||
from nailgun.network import utils as net_utils
|
||||
from nailgun.objects.plugin import ClusterPlugins
|
||||
from nailgun.task.helpers import TaskHelper
|
||||
from nailgun.utils import logs as logs_utils
|
||||
from nailgun.utils import reverse
|
||||
|
@ -527,7 +528,8 @@ class NailgunReceiver(object):
|
|||
message = "{0} Access Zabbix dashboard at {1}".format(
|
||||
message, zabbix_url)
|
||||
|
||||
plugins_msg = cls._make_plugins_success_message(task.cluster.plugins)
|
||||
plugins_msg = cls._make_plugins_success_message(
|
||||
ClusterPlugins.get_enabled(task.cluster.id))
|
||||
if plugins_msg:
|
||||
message = '{0}\n\n{1}'.format(message, plugins_msg)
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@ import copy
|
|||
|
||||
from nailgun.db.sqlalchemy.models import NeutronConfig
|
||||
from nailgun.db.sqlalchemy.models import NovaNetworkConfig
|
||||
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.settings import settings
|
||||
from nailgun.statistics.utils import get_attr_value
|
||||
from nailgun.statistics.utils import WhiteListRule
|
||||
|
@ -170,8 +172,10 @@ class InstallationInfo(object):
|
|||
'node_groups': self.get_node_groups_info(cluster.node_groups),
|
||||
'status': cluster.status,
|
||||
'extensions': cluster.extensions,
|
||||
'attributes': self.get_attributes(cluster.attributes.editable,
|
||||
self.attributes_white_list),
|
||||
'attributes': self.get_attributes(
|
||||
Cluster.get_editable_attributes(cluster),
|
||||
self.attributes_white_list
|
||||
),
|
||||
'vmware_attributes': self.get_attributes(
|
||||
vmware_attributes_editable,
|
||||
self.vmware_attributes_white_list
|
||||
|
@ -188,7 +192,7 @@ class InstallationInfo(object):
|
|||
|
||||
def get_cluster_plugins_info(self, cluster):
|
||||
plugins_info = []
|
||||
for plugin_inst in cluster.plugins:
|
||||
for plugin_inst in ClusterPlugins.get_enabled(cluster.id):
|
||||
plugin_info = {
|
||||
"id": plugin_inst.id,
|
||||
"name": plugin_inst.name,
|
||||
|
|
|
@ -112,13 +112,9 @@ class ClientProvider(object):
|
|||
def credentials(self):
|
||||
if self._credentials is None:
|
||||
cluster_attrs_editable = \
|
||||
objects.Cluster.get_editable_attributes(
|
||||
self.cluster
|
||||
)["editable"]
|
||||
objects.Cluster.get_editable_attributes(self.cluster)
|
||||
|
||||
access_data = cluster_attrs_editable.get(
|
||||
"workloads_collector"
|
||||
)
|
||||
access_data = cluster_attrs_editable.get("workloads_collector")
|
||||
|
||||
if not access_data:
|
||||
# in case there is no section for workloads_collector
|
||||
|
|
|
@ -1234,7 +1234,7 @@ class CheckBeforeDeploymentTask(object):
|
|||
@classmethod
|
||||
def _check_vmware_consistency(cls, task):
|
||||
"""Checks vmware attributes consistency and proper values."""
|
||||
attributes = task.cluster.attributes.editable
|
||||
attributes = objects.Cluster.get_editable_attributes(task.cluster)
|
||||
vmware_attributes = task.cluster.vmware_attributes
|
||||
# Old(< 6.1) clusters haven't vmware support
|
||||
if vmware_attributes:
|
||||
|
|
|
@ -60,6 +60,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 Component
|
||||
from nailgun.objects import MasterNodeSettings
|
||||
from nailgun.objects import Node
|
||||
|
@ -433,7 +434,6 @@ class EnvironmentManager(object):
|
|||
headers=self.default_headers,
|
||||
expect_errors=False
|
||||
)
|
||||
|
||||
plugin = Plugin.get_by_uid(resp.json_body['id'])
|
||||
else:
|
||||
plugin = Plugin.create(plugin_data)
|
||||
|
@ -443,7 +443,7 @@ class EnvironmentManager(object):
|
|||
# Enable plugin for specific cluster
|
||||
if cluster:
|
||||
cluster.plugins.append(plugin)
|
||||
|
||||
ClusterPlugins.set_attributes(cluster.id, plugin.id, enabled=True)
|
||||
return plugin
|
||||
|
||||
def create_component(self, release=None, plugin=None, **kwargs):
|
||||
|
@ -705,7 +705,13 @@ class EnvironmentManager(object):
|
|||
{'repository_path': 'repositories/centos',
|
||||
'version': '2014.2-6.0', 'os': 'centos',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}]}
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
{'repository_path': 'repositories/ubuntu',
|
||||
'version': '2015.1-8.0', 'os': 'ubuntu',
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'},
|
||||
]
|
||||
}
|
||||
|
||||
sample_plugin.update(kwargs)
|
||||
return sample_plugin
|
||||
|
|
|
@ -48,7 +48,7 @@ class TestAttributes(BaseIntegrationTest):
|
|||
attrs = objects.Cluster.get_attributes(cluster_db)
|
||||
self._compare_generated(
|
||||
release.attributes_metadata['generated'],
|
||||
attrs.generated,
|
||||
attrs['generated'],
|
||||
cluster_db
|
||||
)
|
||||
|
||||
|
@ -92,9 +92,9 @@ class TestAttributes(BaseIntegrationTest):
|
|||
headers=self.default_headers
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
attrs = objects.Cluster.get_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs.editable["foo"])
|
||||
attrs.editable.pop('foo')
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs["foo"])
|
||||
attrs.pop('foo')
|
||||
|
||||
# 400 on generated update
|
||||
resp = self.app.put(
|
||||
|
@ -145,10 +145,10 @@ class TestAttributes(BaseIntegrationTest):
|
|||
headers=self.default_headers
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
attrs = objects.Cluster.get_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs.editable["foo"])
|
||||
attrs.editable.pop('foo')
|
||||
self.assertNotEqual(attrs.editable, {})
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs["foo"])
|
||||
attrs.pop('foo')
|
||||
self.assertNotEqual(attrs, {})
|
||||
|
||||
def test_failing_attributes_put(self):
|
||||
cluster_id = self.env.create_cluster(api=True)['id']
|
||||
|
@ -249,8 +249,8 @@ class TestAttributes(BaseIntegrationTest):
|
|||
expect_errors=True
|
||||
)
|
||||
self.assertEqual(200, resp.status_code, resp.body)
|
||||
attrs = objects.Cluster.get_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs.editable["foo"])
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
self.assertEqual("bar", attrs["foo"])
|
||||
# Set attributes to defaults.
|
||||
resp = self.app.put(
|
||||
reverse(
|
||||
|
@ -271,9 +271,8 @@ class TestAttributes(BaseIntegrationTest):
|
|||
def test_attributes_merged_values(self):
|
||||
cluster = self.env.create_cluster(api=True)
|
||||
cluster_db = objects.Cluster.get_by_uid(cluster['id'])
|
||||
attrs = objects.Cluster.get_attributes(cluster_db)
|
||||
orig_attrs = objects.Attributes.merged_attrs(attrs)
|
||||
attrs = objects.Attributes.merged_attrs_values(attrs)
|
||||
orig_attrs = objects.Attributes.merged_attrs(cluster_db.attributes)
|
||||
attrs = objects.Attributes.merged_attrs_values(cluster_db.attributes)
|
||||
for group, group_attrs in orig_attrs.iteritems():
|
||||
for attr, orig_value in group_attrs.iteritems():
|
||||
if group == 'common':
|
||||
|
@ -383,7 +382,7 @@ class TestAttributes(BaseIntegrationTest):
|
|||
def test_editable_attributes_generators(self):
|
||||
self.env.create_cluster(api=True)
|
||||
cluster = self.env.clusters[0]
|
||||
editable = objects.Cluster.get_attributes(cluster).editable
|
||||
editable = objects.Cluster.get_editable_attributes(cluster)
|
||||
self.assertEqual(
|
||||
editable["external_dns"]["dns_list"]["value"],
|
||||
settings.DNS_UPSTREAM
|
||||
|
@ -396,7 +395,7 @@ class TestAttributes(BaseIntegrationTest):
|
|||
def test_workloads_collector_attributes(self):
|
||||
self.env.create_cluster(api=True)
|
||||
cluster = self.env.clusters[0]
|
||||
editable = objects.Cluster.get_attributes(cluster).editable
|
||||
editable = objects.Cluster.get_editable_attributes(cluster)
|
||||
self.assertEqual(
|
||||
editable["workloads_collector"]["enabled"]["value"],
|
||||
True
|
||||
|
@ -655,7 +654,7 @@ class TestVmwareAttributes(BaseIntegrationTest):
|
|||
)
|
||||
|
||||
def _set_use_vcenter(self, cluster):
|
||||
cluster_attrs = objects.Cluster.get_attributes(cluster).editable
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster)
|
||||
cluster_attrs['common']['use_vcenter']['value'] = True
|
||||
objects.Cluster.update_attributes(
|
||||
cluster, {'editable': cluster_attrs})
|
||||
|
@ -666,7 +665,7 @@ class TestVmwareAttributesDefaults(BaseIntegrationTest):
|
|||
def test_get_default_vmware_attributes(self):
|
||||
cluster = self.env.create_cluster(api=True)
|
||||
cluster_db = self.env.clusters[0]
|
||||
cluster_attrs = objects.Cluster.get_attributes(cluster_db).editable
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
cluster_attrs['common']['use_vcenter']['value'] = True
|
||||
objects.Cluster.update_attributes(
|
||||
cluster_db, {'editable': cluster_attrs})
|
||||
|
@ -698,3 +697,102 @@ class TestVmwareAttributesDefaults(BaseIntegrationTest):
|
|||
"Cluster doesn't support vmware configuration",
|
||||
resp.json_body["message"]
|
||||
)
|
||||
|
||||
|
||||
class TestAttributesWithPlugins(BaseIntegrationTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAttributesWithPlugins, self).setUp()
|
||||
|
||||
self.env.create(
|
||||
release_kwargs={
|
||||
'operating_system': consts.RELEASE_OS.ubuntu,
|
||||
'version': '2015.1.0-7.0',
|
||||
},
|
||||
cluster_kwargs={
|
||||
'mode': consts.CLUSTER_MODES.ha_compact,
|
||||
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
||||
'net_segment_type': consts.NEUTRON_SEGMENT_TYPES.vlan,
|
||||
})
|
||||
|
||||
self.cluster = self.env.clusters[0]
|
||||
|
||||
self.plugin_data = {
|
||||
'releases': [
|
||||
{
|
||||
'repository_path': 'repositories/ubuntu',
|
||||
'version': self.cluster.release.version,
|
||||
'os': self.cluster.release.operating_system.lower(),
|
||||
'mode': [self.cluster.mode],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def test_cluster_contains_plugins_attributes(self):
|
||||
self.env.create_plugin(cluster=self.cluster, **self.plugin_data)
|
||||
resp = self.app.get(
|
||||
reverse(
|
||||
'ClusterAttributesHandler',
|
||||
kwargs={'cluster_id': self.cluster['id']}),
|
||||
headers=self.default_headers
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertIn('testing_plugin', resp.json_body['editable'])
|
||||
|
||||
def test_change_plugins_attributes(self):
|
||||
plugin = self.env.create_plugin(cluster=self.cluster,
|
||||
**self.plugin_data)
|
||||
attr = '#{0}_attr'.format(plugin.id)
|
||||
|
||||
def _modify_plugin(enabled=True):
|
||||
return self.app.put(
|
||||
reverse(
|
||||
'ClusterAttributesHandler',
|
||||
kwargs={'cluster_id': self.cluster['id']}),
|
||||
params=jsonutils.dumps({
|
||||
'editable': {
|
||||
'testing_plugin': {
|
||||
'metadata': {
|
||||
'label': 'Test plugin',
|
||||
'toggleable': True,
|
||||
'weight': 70,
|
||||
'enabled': enabled
|
||||
},
|
||||
'plugin_versions': {
|
||||
'type': 'radio',
|
||||
'values': [{
|
||||
'data': str(plugin.id),
|
||||
'description': '',
|
||||
'label': '0.1.0'
|
||||
}],
|
||||
'weight': 10,
|
||||
'value': str(plugin.id),
|
||||
'label': 'Choose a plugin version'
|
||||
},
|
||||
attr: {
|
||||
'type': 'text',
|
||||
'description': 'description',
|
||||
'label': 'label',
|
||||
'value': '1',
|
||||
'weight': 25,
|
||||
'restrictions': [{'action': 'hide'}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
headers=self.default_headers
|
||||
)
|
||||
|
||||
resp = _modify_plugin(enabled=True)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
editable = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
self.assertIn('testing_plugin', editable)
|
||||
self.assertTrue(editable['testing_plugin']['metadata']['enabled'])
|
||||
self.assertEqual('1', editable['testing_plugin']['attr']['value'])
|
||||
|
||||
resp = _modify_plugin(enabled=False)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
editable = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
self.assertIn('testing_plugin', editable)
|
||||
self.assertFalse(editable['testing_plugin']['metadata']['enabled'])
|
||||
self.assertNotIn(attr, editable['testing_plugin'])
|
||||
|
|
|
@ -428,7 +428,7 @@ class TestHandlers(BaseIntegrationTest):
|
|||
self.db.delete(p)
|
||||
self.db.flush()
|
||||
|
||||
attrs = cluster_db.attributes.editable
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
attrs['public_network_assignment']['assign_to_all_nodes']['value'] = \
|
||||
True
|
||||
attrs['provision']['method'] = consts.PROVISION_METHODS.cobbler
|
||||
|
@ -915,7 +915,7 @@ class TestHandlers(BaseIntegrationTest):
|
|||
self.db.delete(p)
|
||||
self.db.flush()
|
||||
|
||||
attrs = cluster_db.attributes.editable
|
||||
attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
attrs['public_network_assignment']['assign_to_all_nodes']['value'] = \
|
||||
True
|
||||
attrs['provision']['method'] = consts.PROVISION_METHODS.cobbler
|
||||
|
|
|
@ -141,6 +141,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)
|
||||
self.db.flush()
|
||||
|
||||
roles = self.app.get(
|
||||
|
@ -162,6 +165,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)
|
||||
self.db.flush()
|
||||
plugin_adapter = adapters.wrap_plugin(plugin)
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ class TestHandlers(BaseIntegrationTest):
|
|||
cluster = self.env.create_cluster(api=False)
|
||||
node = self.env.create_node(cluster_id=cluster.id)
|
||||
|
||||
cluster_attrs = objects.Cluster.get_attributes(cluster).editable
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster)
|
||||
test_hostname = 'test-hostname'
|
||||
cluster_attrs['public_ssl']['hostname']['value'] = test_hostname
|
||||
objects.Cluster.update_attributes(
|
||||
|
|
|
@ -72,6 +72,7 @@ class OrchestratorSerializerTestBase(base.BaseIntegrationTest):
|
|||
def setUp(self):
|
||||
super(OrchestratorSerializerTestBase, self).setUp()
|
||||
self.cluster_mock = mock.MagicMock(pending_release_id=None)
|
||||
self.cluster_mock.id = 0
|
||||
self.cluster_mock.deployment_tasks = []
|
||||
self.cluster_mock.release.deployment_tasks = []
|
||||
|
||||
|
@ -2585,7 +2586,7 @@ class BaseDeploymentSerializer(base.BaseIntegrationTest):
|
|||
|
||||
def check_generate_vmware_attributes_data(self):
|
||||
cluster_db = self.db.query(Cluster).get(self.cluster['id'])
|
||||
cluster_attrs = objects.Cluster.get_attributes(cluster_db).editable
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
cluster_attrs.get('common', {}).setdefault('use_vcenter', {})
|
||||
cluster_attrs['common']['use_vcenter']['value'] = True
|
||||
|
||||
|
@ -2802,7 +2803,7 @@ class TestDeploymentHASerializer61(BaseDeploymentSerializer):
|
|||
|
||||
def test_generate_test_vm_image_data(self):
|
||||
cluster_db = self.db.query(Cluster).get(self.cluster['id'])
|
||||
cluster_attrs = objects.Cluster.get_attributes(cluster_db).editable
|
||||
cluster_attrs = objects.Cluster.get_editable_attributes(cluster_db)
|
||||
cluster_attrs['common'].setdefault('use_vcenter', {})
|
||||
cluster_attrs['common']['use_vcenter']['value'] = True
|
||||
|
||||
|
|
|
@ -131,12 +131,20 @@ class TestDeploymentAttributesSerialization70(
|
|||
namespace: "haproxy"
|
||||
""".format(**custom_network))
|
||||
|
||||
def _add_plugin_network_roles(self):
|
||||
plugin_data = self.env.get_default_plugin_metadata()
|
||||
plugin_data['network_roles_metadata'] = self.plugin_network_roles
|
||||
def _add_plugin_network_roles(self, **kwargs):
|
||||
plugin_data = self.env.get_default_plugin_metadata(releases=[{
|
||||
'repository_path': 'repositories/ubuntu',
|
||||
'version': self.cluster_db.release.version,
|
||||
'os': self.cluster_db.release.operating_system.lower(),
|
||||
'mode': [self.cluster_db.mode],
|
||||
}])
|
||||
plugin_data.update(**kwargs)
|
||||
|
||||
plugin = objects.Plugin.create(plugin_data)
|
||||
self.cluster_db.plugins.append(plugin)
|
||||
self.db.commit()
|
||||
objects.ClusterPlugins.set_attributes(self.cluster_db.id,
|
||||
plugin.id,
|
||||
enabled=True)
|
||||
return plugin
|
||||
|
||||
def test_non_default_bridge_mapping(self):
|
||||
expected_mapping = {
|
||||
|
@ -206,7 +214,8 @@ class TestDeploymentAttributesSerialization70(
|
|||
vlan_start=self.custom_network[
|
||||
'vlan_start'
|
||||
])
|
||||
self._add_plugin_network_roles()
|
||||
self._add_plugin_network_roles(
|
||||
network_roles_metadata=self.plugin_network_roles)
|
||||
self.env.create_node(
|
||||
api=True,
|
||||
cluster_id=cluster['id'],
|
||||
|
@ -561,9 +570,6 @@ class TestPluginDeploymentTasksInjection(base.BaseIntegrationTest):
|
|||
def setUp(self):
|
||||
super(TestPluginDeploymentTasksInjection, self).setUp()
|
||||
|
||||
self.cluster = self._prepare_cluster()
|
||||
|
||||
def _prepare_cluster(self):
|
||||
self.env.create(
|
||||
release_kwargs={
|
||||
'version': '2015.1.0-7.0',
|
||||
|
@ -579,36 +585,25 @@ class TestPluginDeploymentTasksInjection(base.BaseIntegrationTest):
|
|||
'pending_addition': True}
|
||||
]
|
||||
)
|
||||
return self.env.clusters[0]
|
||||
|
||||
self.cluster = self.env.clusters[0]
|
||||
|
||||
self.plugin_data = {
|
||||
'releases': [
|
||||
{
|
||||
'repository_path': 'plugin_test',
|
||||
'version': self.cluster.release.version,
|
||||
'os': self.cluster.release.operating_system.lower(),
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'plugin_test/'
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def prepare_plugins_for_cluster(self, cluster, plugins_kw_list):
|
||||
plugins = [
|
||||
self._create_plugin(**kw)
|
||||
for kw in plugins_kw_list
|
||||
]
|
||||
cluster.plugins.extend(plugins)
|
||||
self.db.flush()
|
||||
|
||||
def _create_plugin(self, **plugin_kwargs):
|
||||
plugin_kwargs.update(
|
||||
{
|
||||
'releases': [
|
||||
{
|
||||
'repository_path': 'plugin_test',
|
||||
'version': self.cluster.release.version,
|
||||
'os':
|
||||
self.cluster.release.operating_system.lower(),
|
||||
'mode': ['ha', 'multinode'],
|
||||
'deployment_scripts_path': 'plugin_test/'
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
plugin_data = self.env.get_default_plugin_metadata(
|
||||
**plugin_kwargs
|
||||
)
|
||||
|
||||
return objects.Plugin.create(plugin_data)
|
||||
for kw in plugins_kw_list:
|
||||
kw.update(self.plugin_data)
|
||||
self.env.create_plugin(cluster=cluster, **kw)
|
||||
|
||||
def _check_pre_deployment_tasks(self, serialized, task_type):
|
||||
self.assertTrue(serialized)
|
||||
|
@ -912,17 +907,30 @@ class TestRolesSerializationWithPlugins(BaseDeploymentSerializer):
|
|||
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
|
||||
'net_segment_type': consts.NEUTRON_SEGMENT_TYPES.vlan,
|
||||
})
|
||||
|
||||
self.cluster = self.env.clusters[0]
|
||||
|
||||
self.plugin_data = {
|
||||
'releases': [
|
||||
{
|
||||
'repository_path': 'repositories/ubuntu',
|
||||
'version': self.cluster.release.version,
|
||||
'os': self.cluster.release.operating_system.lower(),
|
||||
'mode': [self.cluster.mode],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def _get_serializer(self, cluster):
|
||||
return get_serializer_for_cluster(cluster)(AstuteGraph(cluster))
|
||||
|
||||
def test_tasks_were_serialized(self):
|
||||
plugin_data = self.env.get_default_plugin_metadata()
|
||||
plugin_data['roles_metadata'] = self.ROLES
|
||||
plugin_data['deployment_tasks'] = self.DEPLOYMENT_TASKS
|
||||
plugin = objects.Plugin.create(plugin_data)
|
||||
self.cluster.plugins.append(plugin)
|
||||
plugin_data = {
|
||||
'roles_metadata': self.ROLES,
|
||||
'deployment_tasks': self.DEPLOYMENT_TASKS
|
||||
}
|
||||
plugin_data.update(self.plugin_data)
|
||||
self.env.create_plugin(cluster=self.cluster, **plugin_data)
|
||||
|
||||
self.env.create_node(
|
||||
api=True,
|
||||
|
@ -949,11 +957,12 @@ class TestRolesSerializationWithPlugins(BaseDeploymentSerializer):
|
|||
}])
|
||||
|
||||
def test_tasks_were_not_serialized(self):
|
||||
plugin_data = self.env.get_default_plugin_metadata()
|
||||
plugin_data['roles_metadata'] = {}
|
||||
plugin_data['deployment_tasks'] = self.DEPLOYMENT_TASKS
|
||||
plugin = objects.Plugin.create(plugin_data)
|
||||
self.cluster.plugins.append(plugin)
|
||||
plugin_data = {
|
||||
'roles_metadata': {},
|
||||
'deployment_tasks': self.DEPLOYMENT_TASKS
|
||||
}
|
||||
plugin_data.update(self.plugin_data)
|
||||
self.env.create_plugin(cluster=self.cluster, **plugin_data)
|
||||
|
||||
self.env.create_node(
|
||||
api=True,
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
# under the License.
|
||||
|
||||
import mock
|
||||
import uuid
|
||||
|
||||
from nailgun import consts
|
||||
from nailgun.errors import errors
|
||||
from nailgun.objects import ClusterPlugins
|
||||
from nailgun.plugins.adapters import PluginAdapterV3
|
||||
from nailgun.plugins.manager import PluginManager
|
||||
from nailgun.test import base
|
||||
|
@ -157,3 +160,73 @@ class TestPluginManager(base.BaseIntegrationTest):
|
|||
def test_sync_metadata_for_specific_plugin(self, sync_mock):
|
||||
PluginManager.sync_plugins_metadata([self.env.plugins[0].id])
|
||||
self.assertEqual(sync_mock.call_count, 1)
|
||||
|
||||
|
||||
class TestClusterPluginIntegration(base.BaseTestCase):
|
||||
|
||||
_compat_meta = {
|
||||
'releases': [{
|
||||
'os': 'ubuntu',
|
||||
'mode': 'ha',
|
||||
'version': '2015.1-8.0',
|
||||
}]
|
||||
}
|
||||
|
||||
_uncompat_meta = {
|
||||
'releases': [{
|
||||
'os': 'ubuntu',
|
||||
'mode': 'ha',
|
||||
'version': '2014.2-7.0',
|
||||
}]
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestClusterPluginIntegration, self).setUp()
|
||||
|
||||
self.env.create(
|
||||
release_kwargs={
|
||||
'operating_system': consts.RELEASE_OS.ubuntu,
|
||||
'version': '2015.1-8.0'},
|
||||
cluster_kwargs={
|
||||
'mode': consts.CLUSTER_MODES.ha_compact,
|
||||
})
|
||||
self.cluster = self.env.clusters[0]
|
||||
|
||||
def _create_plugin(self, **kwargs):
|
||||
plugin = self.env.create_plugin(name=uuid.uuid4().get_hex(), **kwargs)
|
||||
return plugin
|
||||
|
||||
def test_get_compatible_plugins(self):
|
||||
plugin_a = self._create_plugin(**self._compat_meta)
|
||||
self._create_plugin(**self._uncompat_meta)
|
||||
|
||||
compat_plugins = ClusterPlugins.get_compatible_plugins(self.cluster)
|
||||
self.assertItemsEqual(compat_plugins, [plugin_a])
|
||||
|
||||
def test_get_compatible_plugins_for_new_cluster(self):
|
||||
plugin_a = self._create_plugin(**self._compat_meta)
|
||||
plugin_b = self._create_plugin(**self._compat_meta)
|
||||
self._create_plugin(**self._uncompat_meta)
|
||||
|
||||
self.env.create(
|
||||
cluster_kwargs={
|
||||
'release_id': self.cluster.release.id,
|
||||
'mode': consts.CLUSTER_MODES.ha_compact,
|
||||
})
|
||||
cluster = self.env.clusters[1]
|
||||
|
||||
compat_plugins = ClusterPlugins.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(
|
||||
self.cluster.id, plugin_a.id, enabled=True)
|
||||
|
||||
compat_plugins = ClusterPlugins.get_compatible_plugins(self.cluster)
|
||||
self.assertItemsEqual(compat_plugins, [plugin_a, plugin_b])
|
||||
|
||||
enabled_plugins = ClusterPlugins.get_enabled(self.cluster.id)
|
||||
self.assertItemsEqual(enabled_plugins, [plugin_a])
|
||||
|
|
|
@ -49,14 +49,21 @@ class BasePluginTest(base.BaseIntegrationTest):
|
|||
self.plugin_env_config = self.env.get_default_plugin_env_config()
|
||||
|
||||
def create_plugin(self, sample=None, expect_errors=False):
|
||||
sample = sample or self.sample_plugin
|
||||
resp = self.app.post(
|
||||
base.reverse('PluginCollectionHandler'),
|
||||
jsonutils.dumps(sample),
|
||||
headers=self.default_headers,
|
||||
expect_errors=expect_errors
|
||||
)
|
||||
return resp
|
||||
with mock.patch('nailgun.plugins.adapters.os') as os:
|
||||
with mock.patch('nailgun.plugins.adapters.open',
|
||||
create=True,
|
||||
side_effect=get_config(self.plugin_env_config)):
|
||||
os.access.return_value = True
|
||||
os.path.exists.return_value = True
|
||||
|
||||
sample = sample or self.sample_plugin
|
||||
resp = self.app.post(
|
||||
base.reverse('PluginCollectionHandler'),
|
||||
jsonutils.dumps(sample),
|
||||
headers=self.default_headers,
|
||||
expect_errors=expect_errors
|
||||
)
|
||||
return resp
|
||||
|
||||
def delete_plugin(self, plugin_id, expect_errors=False):
|
||||
resp = self.app.delete(
|
||||
|
@ -89,21 +96,26 @@ class BasePluginTest(base.BaseIntegrationTest):
|
|||
headers=self.default_headers)
|
||||
return resp
|
||||
|
||||
def modify_plugin(self, cluster, plugin_name, enabled):
|
||||
editable_attrs = cluster.attributes.editable
|
||||
def modify_plugin(self, cluster, plugin_name, plugin_id, enabled):
|
||||
editable_attrs = objects.Cluster.get_editable_attributes(
|
||||
cluster, all_plugins_versions=True)
|
||||
editable_attrs[plugin_name]['metadata']['enabled'] = enabled
|
||||
editable_attrs[plugin_name]['plugin_versions']['value'] = \
|
||||
str(plugin_id)
|
||||
|
||||
resp = self.app.put(
|
||||
base.reverse('ClusterAttributesHandler',
|
||||
{'cluster_id': cluster.id}),
|
||||
jsonutils.dumps({'editable': editable_attrs}),
|
||||
headers=self.default_headers)
|
||||
|
||||
return resp
|
||||
|
||||
def enable_plugin(self, cluster, plugin_name):
|
||||
return self.modify_plugin(cluster, plugin_name, True)
|
||||
def enable_plugin(self, cluster, plugin_name, plugin_id):
|
||||
return self.modify_plugin(cluster, plugin_name, plugin_id, True)
|
||||
|
||||
def disable_plugin(self, cluster, plugin_name):
|
||||
return self.modify_plugin(cluster, plugin_name, False)
|
||||
return self.modify_plugin(cluster, plugin_name, None, False)
|
||||
|
||||
def get_pre_hooks(self, cluster):
|
||||
with mock.patch('nailgun.plugins.adapters.glob') as glob:
|
||||
|
@ -162,19 +174,23 @@ class TestPluginsApi(BasePluginTest):
|
|||
def test_env_create_and_load_env_config(self):
|
||||
self.create_plugin()
|
||||
cluster = self.create_cluster()
|
||||
self.assertIn(self.sample_plugin['name'], cluster.attributes.editable)
|
||||
self.assertIn(self.sample_plugin['name'],
|
||||
objects.Cluster.get_editable_attributes(
|
||||
cluster, all_plugins_versions=True))
|
||||
|
||||
def test_enable_disable_plugin(self):
|
||||
resp = self.create_plugin()
|
||||
plugin = objects.Plugin.get_by_uid(resp.json['id'])
|
||||
cluster = self.create_cluster()
|
||||
self.assertEqual(plugin.clusters, [])
|
||||
resp = self.enable_plugin(cluster, plugin.name)
|
||||
self.assertEqual(objects.ClusterPlugins.get_enabled(cluster.id), [])
|
||||
|
||||
resp = self.enable_plugin(cluster, plugin.name, plugin.id)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertIn(cluster, plugin.clusters)
|
||||
self.assertIn(plugin, objects.ClusterPlugins.get_enabled(cluster.id))
|
||||
|
||||
resp = self.disable_plugin(cluster, plugin.name)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(plugin.clusters, [])
|
||||
self.assertEqual(objects.ClusterPlugins.get_enabled(cluster.id), [])
|
||||
|
||||
def test_delete_plugin(self):
|
||||
resp = self.create_plugin()
|
||||
|
@ -185,7 +201,7 @@ class TestPluginsApi(BasePluginTest):
|
|||
resp = self.create_plugin()
|
||||
plugin = objects.Plugin.get_by_uid(resp.json['id'])
|
||||
cluster = self.create_cluster()
|
||||
enable_resp = self.enable_plugin(cluster, plugin.name)
|
||||
enable_resp = self.enable_plugin(cluster, plugin.name, plugin.id)
|
||||
self.assertEqual(enable_resp.status_code, 200)
|
||||
del_resp = self.delete_plugin(resp.json['id'], expect_errors=True)
|
||||
self.assertEqual(del_resp.status_code, 400)
|
||||
|
@ -210,29 +226,67 @@ class TestPluginsApi(BasePluginTest):
|
|||
self.create_plugin()
|
||||
cluster = self.create_cluster()
|
||||
default_attributes = self.default_attributes(cluster)
|
||||
self.assertIn(self.sample_plugin['name'], default_attributes)
|
||||
self.assertIn(self.sample_plugin['name'],
|
||||
default_attributes.json_body['editable'])
|
||||
|
||||
def test_attributes_after_plugin_is_created(self):
|
||||
sample = dict({
|
||||
"attributes_metadata": {
|
||||
"attr_text": {
|
||||
"value": "value",
|
||||
"type": "text",
|
||||
"description": "description",
|
||||
"weight": 25,
|
||||
"label": "label"
|
||||
}
|
||||
}
|
||||
}, **self.sample_plugin)
|
||||
plugin = self.create_plugin(sample=sample).json_body
|
||||
cluster = self.create_cluster()
|
||||
editable = self.default_attributes(cluster).json_body['editable']
|
||||
attr_name = "#{0}_{1}".format(plugin['id'], 'attr_text')
|
||||
self.assertIn(attr_name, editable[self.sample_plugin['name']])
|
||||
|
||||
def test_plugins_multiversioning(self):
|
||||
def create_with_version(version):
|
||||
self.create_plugin(sample=self.env.get_default_plugin_metadata(
|
||||
name='multiversion_plugin', version=version))
|
||||
def create_with_version(plugin_version):
|
||||
response = self.create_plugin(
|
||||
sample=self.env.get_default_plugin_metadata(
|
||||
name='multiversion_plugin',
|
||||
version=plugin_version
|
||||
)
|
||||
)
|
||||
return response.json_body['id']
|
||||
|
||||
def get_num_enabled(cluster_id):
|
||||
return len(objects.ClusterPlugins.get_enabled(cluster_id))
|
||||
|
||||
def get_enabled_version(cluster_id):
|
||||
plugin = objects.ClusterPlugins.get_enabled(cluster_id)[0]
|
||||
return plugin.version
|
||||
|
||||
plugin_ids = []
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1']:
|
||||
create_with_version(version)
|
||||
plugin_ids.append(create_with_version(version))
|
||||
|
||||
cluster = self.create_cluster()
|
||||
# Create new plugin after environment is created
|
||||
create_with_version('5.0.0')
|
||||
self.assertEqual(get_num_enabled(cluster.id), 0)
|
||||
|
||||
self.enable_plugin(cluster, 'multiversion_plugin')
|
||||
self.assertEqual(len(cluster.plugins), 1)
|
||||
enabled_plugin = cluster.plugins[0]
|
||||
# Should be enabled the newest plugin,
|
||||
# at the moment of environment creation
|
||||
self.assertEqual(enabled_plugin.version, '2.0.0')
|
||||
self.enable_plugin(cluster, 'multiversion_plugin', plugin_ids[1])
|
||||
self.assertEqual(get_num_enabled(cluster.id), 1)
|
||||
self.assertEqual(get_enabled_version(cluster.id), '2.0.0')
|
||||
|
||||
# Create new plugin after environment is created
|
||||
plugin_ids.append(create_with_version('5.0.0'))
|
||||
self.assertEqual(len(cluster.plugins), 4)
|
||||
self.assertEqual(get_num_enabled(cluster.id), 1)
|
||||
self.assertEqual(get_enabled_version(cluster.id), '2.0.0')
|
||||
|
||||
self.enable_plugin(cluster, 'multiversion_plugin', plugin_ids[3])
|
||||
self.assertEqual(get_num_enabled(cluster.id), 1)
|
||||
self.assertEqual(get_enabled_version(cluster.id), '5.0.0')
|
||||
|
||||
self.disable_plugin(cluster, 'multiversion_plugin')
|
||||
self.assertEqual(len(cluster.plugins), 0)
|
||||
self.assertEqual(get_num_enabled(cluster.id), 0)
|
||||
|
||||
def test_sync_all_plugins(self):
|
||||
self._create_new_and_old_version_plugins_for_sync()
|
||||
|
@ -268,27 +322,6 @@ class TestPluginsApi(BasePluginTest):
|
|||
resp.json_body["message"],
|
||||
'Problem with loading YAML file')
|
||||
|
||||
@mock.patch('nailgun.objects.cluster.AttributesGenerator')
|
||||
def test_plugin_generator(self, mock_attributes_generator):
|
||||
mock_attributes_generator.test_plugin_generator.return_value = 'test'
|
||||
plugin_name = 'testing_plugin'
|
||||
self.sample_plugin = self.env.get_default_plugin_metadata(
|
||||
name=plugin_name
|
||||
)
|
||||
self.plugin_env_config = \
|
||||
self.env.get_default_plugin_env_config(
|
||||
value={
|
||||
'generator': 'test_plugin_generator',
|
||||
},
|
||||
plugin_name=plugin_name
|
||||
)
|
||||
self.create_plugin()
|
||||
cluster = self.create_cluster()
|
||||
self.assertIn(self.sample_plugin['name'], cluster.attributes.editable)
|
||||
plugin_dict = cluster.attributes.editable[plugin_name]
|
||||
value = plugin_dict['%s_text' % plugin_name]['value']
|
||||
self.assertEqual(value, 'test')
|
||||
|
||||
def _create_new_and_old_version_plugins_for_sync(self):
|
||||
plugin_ids = []
|
||||
|
||||
|
@ -360,7 +393,9 @@ class TestPrePostHooks(BasePluginTest):
|
|||
{'roles': ['controller'], 'pending_addition': True},
|
||||
{'roles': ['compute'], 'pending_addition': True}])
|
||||
objects.NodeCollection.prepare_for_deployment(self.cluster.nodes)
|
||||
self.enable_plugin(self.cluster, self.sample_plugin['name'])
|
||||
self.enable_plugin(self.cluster,
|
||||
self.sample_plugin['name'],
|
||||
resp.json['id'])
|
||||
|
||||
def tearDown(self):
|
||||
self._requests_mock.stop()
|
||||
|
|
|
@ -274,7 +274,8 @@ class TestInstallationInfo(BaseTestCase):
|
|||
|
||||
cluster_plugin_kwargs = {
|
||||
"cluster_id": cluster.id,
|
||||
"plugin_id": plugin_obj.id
|
||||
"plugin_id": plugin_obj.id,
|
||||
"enabled": True
|
||||
}
|
||||
cluster_plugin = plugins.ClusterPlugins(**cluster_plugin_kwargs)
|
||||
|
||||
|
|
|
@ -49,7 +49,15 @@ class BaseComponentTestCase(base.BaseTestCase):
|
|||
class TestComponentCollection(BaseComponentTestCase):
|
||||
|
||||
def test_get_all_by_release(self):
|
||||
self.incompatible_plugin = self.env.create_plugin()
|
||||
self.incompatible_plugin = self.env.create_plugin(
|
||||
fuel_version=['6.0'],
|
||||
releases=[{
|
||||
'repository_path': 'repositories/centos',
|
||||
'version': '2014.2-6.0',
|
||||
'os': 'centos',
|
||||
'mode': ['ha'],
|
||||
'deployment_scripts_path': 'deployment_scripts/'}]
|
||||
)
|
||||
self.incompatible_release = self.env.create_release(
|
||||
operating_system='Ubuntu',
|
||||
modes=[consts.CLUSTER_MODES.ha_compact])
|
||||
|
|
|
@ -33,7 +33,7 @@ _prepare_revision = '1e50a4903910'
|
|||
_test_revision = '43b2cb64dae6'
|
||||
|
||||
|
||||
def setup_module(module):
|
||||
def setup_module():
|
||||
dropdb()
|
||||
alembic.command.upgrade(ALEMBIC_CONFIG, _prepare_revision)
|
||||
prepare()
|
||||
|
@ -103,6 +103,92 @@ def prepare():
|
|||
'net_l23_provider': 'ovs'
|
||||
}])
|
||||
|
||||
result = db.execute(
|
||||
meta.tables['plugins'].insert(),
|
||||
[{
|
||||
'name': 'test_plugin_a',
|
||||
'title': 'Test plugin A',
|
||||
'version': '1.0.0',
|
||||
'description': 'Test plugin A for Fuel',
|
||||
'homepage': 'http://fuel_plugins.test_plugin.com',
|
||||
'package_version': '3.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(['6.1', '7.0']),
|
||||
}]
|
||||
)
|
||||
pluginid_a = result.inserted_primary_key[0]
|
||||
|
||||
result = db.execute(
|
||||
meta.tables['plugins'].insert(),
|
||||
[{
|
||||
'name': 'test_plugin_b',
|
||||
'title': 'Test plugin B',
|
||||
'version': '1.0.0',
|
||||
'description': 'Test plugin B for Fuel',
|
||||
'homepage': 'http://fuel_plugins.test_plugin.com',
|
||||
'package_version': '3.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(['6.1', '7.0']),
|
||||
}]
|
||||
)
|
||||
pluginid_b = result.inserted_primary_key[0]
|
||||
|
||||
db.execute(
|
||||
meta.tables['cluster_plugins'].insert(),
|
||||
[
|
||||
{
|
||||
'cluster_id': clusterid,
|
||||
'plugin_id': pluginid_a
|
||||
},
|
||||
{
|
||||
'cluster_id': clusterid,
|
||||
'plugin_id': pluginid_b
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
db.execute(
|
||||
meta.tables['attributes'].insert(),
|
||||
[{
|
||||
'cluster_id': clusterid,
|
||||
'editable': jsonutils.dumps({
|
||||
'test_plugin_a': {
|
||||
'metadata': {
|
||||
'plugin_id': pluginid_a,
|
||||
'enabled': True,
|
||||
'toggleable': True,
|
||||
'weight': 70,
|
||||
},
|
||||
'attribute': {
|
||||
'value': 'value',
|
||||
'type': 'text',
|
||||
'description': 'description',
|
||||
'weight': 25,
|
||||
'label': 'label'
|
||||
}
|
||||
},
|
||||
'test_plugin_b': {
|
||||
'metadata': {
|
||||
'plugin_id': pluginid_b,
|
||||
'enabled': False,
|
||||
'toggleable': True,
|
||||
'weight': 80,
|
||||
}
|
||||
}
|
||||
}),
|
||||
'generated': jsonutils.dumps({}),
|
||||
}])
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
|
@ -416,3 +502,56 @@ class TestNeutronConfigInternalFloatingNames(base.BaseAlembicMigrationTest):
|
|||
|
||||
self.assertEqual('net04', neutron_config['internal_name'])
|
||||
self.assertEqual('net04_ext', neutron_config['floating_name'])
|
||||
|
||||
|
||||
class TestClusterPluginsMigration(base.BaseAlembicMigrationTest):
|
||||
|
||||
def _get_enabled(self, plugin_name):
|
||||
plugins = self.meta.tables['plugins']
|
||||
cluster_plugins = self.meta.tables['cluster_plugins']
|
||||
|
||||
query = sa.select([cluster_plugins.c.enabled])\
|
||||
.select_from(
|
||||
sa.join(
|
||||
cluster_plugins, plugins,
|
||||
cluster_plugins.c.plugin_id == plugins.c.id))\
|
||||
.where(plugins.c.name == plugin_name)
|
||||
return db.execute(query).fetchone()[0]
|
||||
|
||||
def test_plugin_a_is_enabled(self):
|
||||
enabled = self._get_enabled('test_plugin_a')
|
||||
self.assertTrue(enabled)
|
||||
|
||||
def test_plugin_b_is_disabled(self):
|
||||
enabled = self._get_enabled('test_plugin_b')
|
||||
self.assertFalse(enabled)
|
||||
|
||||
def test_moving_plugin_attributes(self):
|
||||
clusters = self.meta.tables['clusters']
|
||||
attributes = self.meta.tables['attributes']
|
||||
plugins = self.meta.tables['plugins']
|
||||
cluster_plugins = self.meta.tables['cluster_plugins']
|
||||
|
||||
query = sa.select([attributes.c.editable])\
|
||||
.select_from(
|
||||
sa.join(
|
||||
attributes, clusters,
|
||||
attributes.c.cluster_id == clusters.c.id))
|
||||
result = jsonutils.loads(db.execute(query).fetchone()[0])
|
||||
self.assertItemsEqual(result, {})
|
||||
|
||||
query = sa.select([cluster_plugins.c.attributes])\
|
||||
.select_from(
|
||||
sa.join(
|
||||
cluster_plugins, plugins,
|
||||
cluster_plugins.c.plugin_id == plugins.c.id))\
|
||||
.where(plugins.c.name == 'test_plugin_a')
|
||||
result = jsonutils.loads(db.execute(query).fetchone()[0])
|
||||
self.assertNotIn('metadata', result)
|
||||
self.assertItemsEqual(result['attribute'], {
|
||||
'value': 'value',
|
||||
'type': 'text',
|
||||
'description': 'description',
|
||||
'weight': 25,
|
||||
'label': 'label'
|
||||
})
|
||||
|
|
|
@ -15,23 +15,59 @@
|
|||
# under the License.
|
||||
|
||||
from nailgun import consts
|
||||
from nailgun.objects import ClusterPlugins
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.objects import PluginCollection
|
||||
from nailgun.test import base
|
||||
import sqlalchemy as sa
|
||||
import uuid
|
||||
|
||||
|
||||
class TestPluginCollection(base.BaseTestCase):
|
||||
class ExtraFunctions(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPluginCollection, self).setUp()
|
||||
self.release = self.env.create_release(
|
||||
version='2015.1-8.0',
|
||||
operating_system='Ubuntu',
|
||||
modes=[consts.CLUSTER_MODES.ha_compact])
|
||||
self.plugin_ids = self._create_test_plugins()
|
||||
def _create_test_plugins(self):
|
||||
plugin_ids = []
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1', '3.0.0']:
|
||||
plugin_data = self.env.get_default_plugin_metadata(
|
||||
version=version,
|
||||
name='multiversion_plugin')
|
||||
plugin = Plugin.create(plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
single_plugin_data = self.env.get_default_plugin_metadata(
|
||||
name='single_plugin')
|
||||
plugin = Plugin.create(single_plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
incompatible_plugin_data = self.env.get_default_plugin_metadata(
|
||||
name='incompatible_plugin',
|
||||
releases=[]
|
||||
)
|
||||
plugin = Plugin.create(incompatible_plugin_data)
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
return plugin_ids
|
||||
|
||||
def _create_test_cluster(self):
|
||||
self.env.create(
|
||||
cluster_kwargs={'mode': consts.CLUSTER_MODES.multinode},
|
||||
release_kwargs={
|
||||
'name': uuid.uuid4().get_hex(),
|
||||
'version': '2015.1-8.0',
|
||||
'operating_system': 'Ubuntu',
|
||||
'modes': [consts.CLUSTER_MODES.multinode,
|
||||
consts.CLUSTER_MODES.ha_compact]})
|
||||
|
||||
return self.env.clusters[0]
|
||||
|
||||
|
||||
class TestPluginCollection(ExtraFunctions):
|
||||
|
||||
def test_all_newest(self):
|
||||
self._create_test_plugins()
|
||||
|
||||
newest_plugins = PluginCollection.all_newest()
|
||||
self.assertEqual(len(newest_plugins), 2)
|
||||
self.assertEqual(len(newest_plugins), 3)
|
||||
|
||||
single_plugin = filter(
|
||||
lambda p: p.name == 'single_plugin',
|
||||
|
@ -46,21 +82,62 @@ class TestPluginCollection(base.BaseTestCase):
|
|||
self.assertEqual(multiversion_plugin[0].version, '3.0.0')
|
||||
|
||||
def test_get_by_uids(self):
|
||||
ids = self.plugin_ids[:2]
|
||||
plugin_ids = self._create_test_plugins()
|
||||
ids = plugin_ids[:2]
|
||||
plugins = PluginCollection.get_by_uids(ids)
|
||||
self.assertEqual(len(list(plugins)), 2)
|
||||
self.assertListEqual(
|
||||
[plugin.id for plugin in plugins], ids)
|
||||
|
||||
def _create_test_plugins(self):
|
||||
plugin_ids = []
|
||||
for version in ['1.0.0', '2.0.0', '0.0.1', '3.0.0']:
|
||||
plugin = self.env.create_plugin(
|
||||
version=version,
|
||||
name='multiversion_plugin')
|
||||
plugin_ids.append(plugin.id)
|
||||
|
||||
plugin = self.env.create_plugin(name='single_plugin')
|
||||
plugin_ids.append(plugin.id)
|
||||
class TestClusterPlugins(ExtraFunctions):
|
||||
|
||||
return plugin_ids
|
||||
def test_connect_to_cluster(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
self._create_test_plugins()
|
||||
self._create_test_cluster()
|
||||
cluster_plugins = self.db.execute(
|
||||
meta.tables['cluster_plugins'].select()
|
||||
).fetchall()
|
||||
self.assertEqual(len(cluster_plugins), 5)
|
||||
|
||||
def test_set_plugin_attributes(self):
|
||||
meta = base.reflect_db_metadata()
|
||||
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)
|
||||
|
||||
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)
|
||||
).fetchone()
|
||||
self.assertTrue(enabled[0])
|
||||
|
||||
def test_get_connected_plugins(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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
enabled_plugin = ClusterPlugins.get_enabled(cluster.id)[0].id
|
||||
self.assertEqual(enabled_plugin, plugin_id)
|
||||
|
|
|
@ -940,8 +940,11 @@ class TestClusterObject(BaseTestCase):
|
|||
cluster = self.env.create_cluster(api=False)
|
||||
|
||||
for kw in plugins_kw_list:
|
||||
cluster.plugins.append(objects.Plugin.create(kw))
|
||||
|
||||
plugin = objects.Plugin.create(kw)
|
||||
cluster.plugins.append(plugin)
|
||||
objects.ClusterPlugins.set_attributes(cluster.id,
|
||||
plugin.id,
|
||||
enabled=True)
|
||||
return cluster
|
||||
|
||||
def _get_network_role_metadata(self, **kwargs):
|
||||
|
@ -1293,8 +1296,9 @@ class TestClusterObjectGetRoles(BaseTestCase):
|
|||
roles_metadata=roles_metadata,
|
||||
))
|
||||
self.cluster.plugins.append(plugin)
|
||||
self.db.flush()
|
||||
|
||||
objects.ClusterPlugins.set_attributes(self.cluster.id,
|
||||
plugin.id,
|
||||
enabled=True)
|
||||
return plugin
|
||||
|
||||
def test_no_plugins_no_additional_roles(self):
|
||||
|
|
|
@ -23,6 +23,7 @@ from nailgun import consts
|
|||
from nailgun.db import db
|
||||
from nailgun.errors import errors
|
||||
from nailgun.expression import Expression
|
||||
from nailgun.objects import ClusterPlugins
|
||||
from nailgun.objects import Component
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.plugins import adapters
|
||||
|
@ -65,7 +66,7 @@ class TestPluginBase(base.BaseTestCase):
|
|||
self.env.create(
|
||||
cluster_kwargs={'mode': consts.CLUSTER_MODES.multinode},
|
||||
release_kwargs={
|
||||
'version': '2014.2-6.0',
|
||||
'version': '2015.1-8.0',
|
||||
'operating_system': 'Ubuntu',
|
||||
'modes': [consts.CLUSTER_MODES.multinode,
|
||||
consts.CLUSTER_MODES.ha_compact]})
|
||||
|
@ -77,30 +78,12 @@ class TestPluginBase(base.BaseTestCase):
|
|||
|
||||
db().flush()
|
||||
|
||||
@mock.patch('nailgun.plugins.adapters.open', create=True)
|
||||
@mock.patch('nailgun.plugins.adapters.os.access')
|
||||
@mock.patch('nailgun.plugins.adapters.os.path.exists')
|
||||
def test_get_plugin_attributes(self, mexists, maccess, mopen):
|
||||
"""Should load attributes from environment_config
|
||||
|
||||
Attributes should contain provided attributes by plugin and
|
||||
also generated metadata
|
||||
"""
|
||||
maccess.return_value = True
|
||||
mexists.return_value = True
|
||||
mopen.side_effect = self.get_config
|
||||
attributes = self.plugin_adapter.get_plugin_attributes(self.cluster)
|
||||
self.assertEqual(
|
||||
attributes['testing_plugin']['plugin_name_text'],
|
||||
self.env_config['attributes']['plugin_name_text'])
|
||||
self.assertEqual(
|
||||
attributes['testing_plugin']['metadata'],
|
||||
self.plugin_adapter.default_metadata)
|
||||
|
||||
def test_plugin_release_versions(self):
|
||||
"""Should return set of all versions this plugin is applicable to"""
|
||||
self.assertEqual(
|
||||
self.plugin_adapter.plugin_release_versions, set(['2014.2-6.0']))
|
||||
self.plugin_adapter.plugin_release_versions,
|
||||
set(['2014.2-6.0', '2015.1-8.0'])
|
||||
)
|
||||
|
||||
def test_full_name(self):
|
||||
"""Plugin full name should be made from name and version."""
|
||||
|
@ -271,7 +254,8 @@ class TestPluginV3(TestPluginBase):
|
|||
getattr(self.plugin, key), val)
|
||||
|
||||
self.assertEqual(
|
||||
self.plugin.attributes_metadata, attributes_metadata)
|
||||
self.plugin.attributes_metadata,
|
||||
attributes_metadata['attributes'])
|
||||
self.assertEqual(
|
||||
self.plugin.roles_metadata, roles_metadata)
|
||||
self.assertEqual(
|
||||
|
@ -318,7 +302,8 @@ class TestPluginV4(TestPluginBase):
|
|||
getattr(self.plugin, key), val)
|
||||
|
||||
self.assertEqual(
|
||||
self.plugin.attributes_metadata, attributes_metadata)
|
||||
self.plugin.attributes_metadata,
|
||||
attributes_metadata['attributes'])
|
||||
self.assertEqual(
|
||||
self.plugin.roles_metadata, roles_metadata)
|
||||
self.assertEqual(
|
||||
|
@ -349,10 +334,10 @@ class TestPluginV4(TestPluginBase):
|
|||
self.assertEqual(component.plugin_id, self.plugin.id)
|
||||
|
||||
|
||||
class TestClusterCompatiblityValidation(base.BaseTestCase):
|
||||
class TestClusterCompatibilityValidation(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClusterCompatiblityValidation, self).setUp()
|
||||
super(TestClusterCompatibilityValidation, self).setUp()
|
||||
self.plugin = Plugin.create(self.env.get_default_plugin_metadata(
|
||||
releases=[{
|
||||
'version': '2014.2-6.0',
|
||||
|
@ -367,7 +352,7 @@ class TestClusterCompatiblityValidation(base.BaseTestCase):
|
|||
|
||||
def validate_with_cluster(self, **kwargs):
|
||||
cluster = self.cluster_mock(**kwargs)
|
||||
return self.plugin_adapter.validate_cluster_compatibility(cluster)
|
||||
return ClusterPlugins.validate_compatibility(cluster, self.plugin)
|
||||
|
||||
def test_validation_ubuntu_ha(self):
|
||||
self.assertTrue(self.validate_with_cluster(
|
||||
|
|
|
@ -19,6 +19,7 @@ from mock import patch
|
|||
|
||||
from nailgun import consts
|
||||
from nailgun.errors import errors
|
||||
from nailgun.objects import ClusterPlugins
|
||||
from nailgun.objects import Plugin
|
||||
from nailgun.rpc.receiver import NailgunReceiver
|
||||
from nailgun.test import base
|
||||
|
@ -44,6 +45,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)
|
||||
|
||||
self.task = self.env.create_task(
|
||||
name=consts.TASK_NAMES.deployment,
|
||||
|
|
|
@ -262,14 +262,13 @@ class TestAttributesRestriction(base.BaseTestCase):
|
|||
def test_check_with_invalid_values(self):
|
||||
objects.Cluster.update_attributes(
|
||||
self.cluster, self.attributes_data)
|
||||
attributes = objects.Cluster.get_attributes(self.cluster)
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
models = {
|
||||
'settings': attributes.editable,
|
||||
'default': attributes.editable,
|
||||
'settings': attributes,
|
||||
'default': attributes,
|
||||
}
|
||||
|
||||
errs = AttributesRestriction.check_data(
|
||||
models, attributes.editable)
|
||||
errs = AttributesRestriction.check_data(models, attributes)
|
||||
self.assertItemsEqual(
|
||||
errs, ['Invalid username', 'Invalid tenant name'])
|
||||
|
||||
|
@ -280,14 +279,13 @@ class TestAttributesRestriction(base.BaseTestCase):
|
|||
|
||||
objects.Cluster.update_attributes(
|
||||
self.cluster, self.attributes_data)
|
||||
attributes = objects.Cluster.get_attributes(self.cluster)
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
models = {
|
||||
'settings': attributes.editable,
|
||||
'default': attributes.editable,
|
||||
'settings': attributes,
|
||||
'default': attributes,
|
||||
}
|
||||
|
||||
errs = AttributesRestriction.check_data(
|
||||
models, attributes.editable)
|
||||
errs = AttributesRestriction.check_data(models, attributes)
|
||||
self.assertListEqual(errs, [])
|
||||
|
||||
|
||||
|
@ -303,7 +301,7 @@ class TestVmwareAttributesRestriction(base.BaseTestCase):
|
|||
self.vm_data = self.env.read_fixtures(['vmware_attributes'])[0]
|
||||
|
||||
def test_check_data_with_empty_values_without_restrictions(self):
|
||||
attributes = objects.Cluster.get_attributes(self.cluster).editable
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
attributes['common']['use_vcenter']['value'] = True
|
||||
attributes['storage']['images_vcenter']['value'] = True
|
||||
vmware_attributes = self.vm_data.copy()
|
||||
|
@ -355,7 +353,7 @@ class TestVmwareAttributesRestriction(base.BaseTestCase):
|
|||
|
||||
def test_check_data_with_invalid_values_without_restrictions(self):
|
||||
# Disable restrictions
|
||||
attributes = objects.Cluster.get_attributes(self.cluster).editable
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
attributes['common']['use_vcenter']['value'] = True
|
||||
attributes['storage']['images_vcenter']['value'] = True
|
||||
# value data taken from fixture one cluster of
|
||||
|
@ -376,7 +374,7 @@ class TestVmwareAttributesRestriction(base.BaseTestCase):
|
|||
self.assertItemsEqual(errs, ['Empty cluster'])
|
||||
|
||||
def test_check_data_with_invalid_values_and_with_restrictions(self):
|
||||
attributes = objects.Cluster.get_attributes(self.cluster).editable
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
# fixture have restrictions enabled for glance that's why
|
||||
# only 'Empty cluster' should returned
|
||||
vmware_attributes = self.vm_data.copy()
|
||||
|
@ -395,7 +393,7 @@ class TestVmwareAttributesRestriction(base.BaseTestCase):
|
|||
self.assertItemsEqual(errs, ['Empty cluster'])
|
||||
|
||||
def test_check_data_with_valid_values_and_with_restrictions(self):
|
||||
attributes = objects.Cluster.get_attributes(self.cluster).editable
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
vmware_attributes = self.vm_data.copy()
|
||||
# Set valid data for clusters
|
||||
for i, azone in enumerate(
|
||||
|
@ -419,7 +417,7 @@ class TestVmwareAttributesRestriction(base.BaseTestCase):
|
|||
|
||||
def test_check_data_with_valid_values_and_without_restrictions(self):
|
||||
# Disable restrictions
|
||||
attributes = objects.Cluster.get_attributes(self.cluster).editable
|
||||
attributes = objects.Cluster.get_editable_attributes(self.cluster)
|
||||
attributes['common']['use_vcenter']['value'] = True
|
||||
attributes['storage']['images_vcenter']['value'] = True
|
||||
vmware_attributes = self.vm_data.copy()
|
||||
|
|
Loading…
Reference in New Issue