Raise error once there's no deployment tasks in db
Added new exception 'NoDeploymentTasks' which is raised when user is trying to manually deploy cluster whose release has empty 'deployment_tasks' and 'fuel_version' >= 6.1 Added loading of fake deployment tasks in both test Environment class and 'loaddefault' command - so it also works in fake mode. Change-Id: Ibaee6912cfbca27dbb35c4f9c09feb74c81b02a1 Closes-Bug: #1448066
This commit is contained in:
parent
0c66198156
commit
6b6219e74e
|
@ -92,8 +92,8 @@ def load_db_parsers(subparsers):
|
|||
)
|
||||
subparsers.add_parser(
|
||||
'loaddefault',
|
||||
help='load data from default fixtures '
|
||||
'(settings.FIXTURES_TO_IPLOAD)'
|
||||
help='load data from default fixtures (settings.FIXTURES_TO_UPLOAD) '
|
||||
'and apply fake deployment tasks for all releases in database'
|
||||
)
|
||||
|
||||
|
||||
|
@ -173,12 +173,23 @@ def action_loaddata(params):
|
|||
logger.info("Done")
|
||||
|
||||
|
||||
def action_loadfakedeploymenttasks(params):
|
||||
from nailgun.db.sqlalchemy import fixman
|
||||
from nailgun.logger import logger
|
||||
|
||||
logger.info("Applying fake deployment tasks to all releases...")
|
||||
fixman.load_fake_deployment_tasks()
|
||||
logger.info("Done")
|
||||
|
||||
|
||||
def action_loaddefault(params):
|
||||
from nailgun.db.sqlalchemy import fixman
|
||||
from nailgun.logger import logger
|
||||
|
||||
logger.info("Uploading fixture...")
|
||||
fixman.upload_fixtures()
|
||||
logger.info("Applying fake deployment tasks to all releases...")
|
||||
fixman.load_fake_deployment_tasks()
|
||||
logger.info("Done")
|
||||
|
||||
|
||||
|
@ -270,7 +281,7 @@ def action_run(params):
|
|||
|
||||
if params.authentication_method:
|
||||
auth_method = params.authentication_method
|
||||
settings.AUTH.update({'AUTHENTICATION_METHOD' : auth_method})
|
||||
settings.AUTH.update({'AUTHENTICATION_METHOD': auth_method})
|
||||
|
||||
if params.config_file:
|
||||
settings.update_from_file(params.config_file)
|
||||
|
|
|
@ -163,7 +163,6 @@ class BaseHandler(object):
|
|||
try:
|
||||
data = kwargs.pop('data', web.data())
|
||||
method = validate_method or cls.validator.validate
|
||||
|
||||
valid_data = method(data, **kwargs)
|
||||
except (
|
||||
errors.InvalidInterfacesInfo,
|
||||
|
@ -185,6 +184,7 @@ class BaseHandler(object):
|
|||
except (
|
||||
errors.InvalidData,
|
||||
errors.NodeOffline,
|
||||
errors.NoDeploymentTasks,
|
||||
errors.UnavailableRelease,
|
||||
errors.CannotDelete
|
||||
) as exc:
|
||||
|
@ -547,6 +547,7 @@ class DeferredTaskHandler(BaseHandler):
|
|||
raise self.http(409, exc.message)
|
||||
except (
|
||||
errors.DeploymentNotRunning,
|
||||
errors.NoDeploymentTasks,
|
||||
errors.WrongNodeStatus,
|
||||
errors.UnavailableRelease,
|
||||
) as exc:
|
||||
|
|
|
@ -274,6 +274,8 @@ class BaseDeploySelectedNodes(SelectedNodesBase):
|
|||
self.checked_data(self.validator.validate_nodes_to_deploy,
|
||||
nodes=nodes_to_deploy, cluster_id=cluster.id)
|
||||
|
||||
self.checked_data(self.validator.validate_release, cluster=cluster)
|
||||
|
||||
return nodes_to_deploy
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ from jsonschema.exceptions import ValidationError
|
|||
from oslo.serialization import jsonutils
|
||||
|
||||
from nailgun.errors import errors
|
||||
from nailgun import objects
|
||||
|
||||
|
||||
class BasicValidator(object):
|
||||
|
@ -91,6 +92,21 @@ class BasicValidator(object):
|
|||
# about internal attributes with error description.
|
||||
raise errors.InvalidData(str(exc))
|
||||
|
||||
@classmethod
|
||||
def validate_release(cls, data=None, cluster=None):
|
||||
"""Validate if deployment tasks are present in db
|
||||
|
||||
:param cluster: Cluster instance
|
||||
:raises NoDeploymentTasks:
|
||||
"""
|
||||
if (cluster and objects.Release.is_granular_enabled(cluster.release)
|
||||
and not objects.Cluster.get_deployment_tasks(cluster)):
|
||||
raise errors.NoDeploymentTasks(
|
||||
"Deployment tasks not found for '{0}' release in the "
|
||||
"database. Please upload them. If you're operating "
|
||||
"from Fuel Master node, please check '/etc/puppet' "
|
||||
"directory.".format(cluster.release.name))
|
||||
|
||||
|
||||
class BaseDefferedTaskValidator(BasicValidator):
|
||||
|
||||
|
|
|
@ -217,6 +217,7 @@ class ClusterChangesValidator(BaseDefferedTaskValidator):
|
|||
|
||||
@classmethod
|
||||
def validate(cls, cluster):
|
||||
cls.validate_release(cluster=cluster)
|
||||
ProvisionSelectedNodesValidator.validate_provision(None, cluster)
|
||||
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ class DeploySelectedNodesValidator(NodesFilterValidator):
|
|||
in proper state
|
||||
|
||||
:param data: raw json data, usually web.data(). Is not used here
|
||||
and is needed for mantaining consistency of data validating logic
|
||||
and is needed for maintaining consistency of data validating logic
|
||||
:param nodes: list of node objects state of which to be checked
|
||||
:param cluster_id: id of the cluster for which operation is performed
|
||||
"""
|
||||
|
|
|
@ -39,6 +39,25 @@ def capitalize_model_name(model_name):
|
|||
return ''.join(map(lambda s: s.capitalize(), model_name.split('_')))
|
||||
|
||||
|
||||
def load_fake_deployment_tasks(apply_to_db=True, commit=True):
|
||||
"""Load fake deployment tasks
|
||||
|
||||
:param apply_to_db: if True applying to all releases in db
|
||||
:param commit: boolean
|
||||
"""
|
||||
fxtr_path = os.path.join(get_base_fixtures_path(), 'deployment_tasks.yaml')
|
||||
with open(fxtr_path) as f:
|
||||
deployment_tasks = yaml.load(f)
|
||||
|
||||
if apply_to_db:
|
||||
for rel in db().query(models.Release).all():
|
||||
rel.deployment_tasks = deployment_tasks
|
||||
if commit:
|
||||
db().commit()
|
||||
else:
|
||||
return deployment_tasks
|
||||
|
||||
|
||||
def template_fixture(fileobj, **kwargs):
|
||||
if not kwargs.get('settings'):
|
||||
kwargs["settings"] = settings
|
||||
|
@ -183,11 +202,19 @@ def upload_fixture(fileobj, loader=None):
|
|||
db().commit()
|
||||
|
||||
|
||||
def upload_fixtures():
|
||||
fixtures_paths = [
|
||||
def get_base_fixtures_path():
|
||||
return os.path.join(os.path.dirname(__file__), '..', '..', 'fixtures')
|
||||
|
||||
|
||||
def get_all_fixtures_paths():
|
||||
return [
|
||||
'/etc/nailgun/fixtures',
|
||||
os.path.join(os.path.dirname(__file__), '..', '..', 'fixtures')
|
||||
get_base_fixtures_path(),
|
||||
]
|
||||
|
||||
|
||||
def upload_fixtures():
|
||||
fixtures_paths = get_all_fixtures_paths()
|
||||
for orig_path in settings.FIXTURES_TO_UPLOAD:
|
||||
if os.path.isabs(orig_path):
|
||||
path = orig_path
|
||||
|
|
|
@ -38,6 +38,7 @@ default_messages = {
|
|||
"CheckBeforeDeploymentError": "Pre-Deployment check wasn't successful",
|
||||
"DeploymentAlreadyStarted": "Deployment already started",
|
||||
"DeploymentNotRunning": "Deployment is not running",
|
||||
"NoDeploymentTasks": "Deployment tasks not found for specific release in the database",
|
||||
"DeletionAlreadyStarted": "Environment removal already started",
|
||||
"StopAlreadyRunning": "Stopping deployment already initiated",
|
||||
"FailedProvisioning": "Failed to start provisioning",
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
- id: deploy_start
|
||||
type: stage
|
||||
|
||||
- id: deploy_end
|
||||
type: stage
|
||||
requires: [deploy_start]
|
||||
|
||||
- id: primary-controller
|
||||
type: group
|
||||
role: [primary-controller]
|
||||
required_for: [deploy_end]
|
||||
requires: [deploy_start]
|
||||
parameters:
|
||||
strategy:
|
||||
type: one_by_one
|
||||
- id: controller
|
||||
type: group
|
||||
role: [controller]
|
||||
requires: [primary-controller]
|
||||
required_for: [deploy_end]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
amount: 6
|
||||
- id: cinder
|
||||
type: group
|
||||
role: [cinder]
|
||||
requires: [controller]
|
||||
required_for: [deploy_end]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
- id: compute
|
||||
type: group
|
||||
role: [compute]
|
||||
requires: [controller]
|
||||
required_for: [deploy_end]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
- id: zabbix-server
|
||||
type: group
|
||||
role: [zabbix-server]
|
||||
required_for: [deploy_end]
|
||||
requires: [deploy_start]
|
||||
parameters:
|
||||
strategy:
|
||||
type: one_by_one
|
||||
- id: mongo
|
||||
type: group
|
||||
role: [mongo]
|
||||
requires: [zabbix-server]
|
||||
required_for: [deploy_end, primary-controller, controller]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
- id: primary-mongo
|
||||
type: group
|
||||
role: [primary-mongo]
|
||||
requires: [mongo]
|
||||
required_for: [deploy_end, primary-controller, controller]
|
||||
parameters:
|
||||
strategy:
|
||||
type: one_by_one
|
||||
- id: ceph-osd
|
||||
type: group
|
||||
role: [ceph-osd]
|
||||
requires: [controller]
|
||||
required_for: [deploy_end]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
- id: base-os
|
||||
type: group
|
||||
role: [base-os]
|
||||
required_for: [deploy_end]
|
||||
parameters:
|
||||
strategy:
|
||||
type: parallel
|
||||
|
||||
- id: deploy_legacy
|
||||
type: puppet
|
||||
groups: [primary-controller, controller,
|
||||
cinder, compute, ceph-osd,
|
||||
zabbix-server, primary-mongo, mongo]
|
||||
required_for: [deploy_end]
|
||||
requires: [deploy_start]
|
||||
parameters:
|
||||
puppet_manifest: /etc/puppet/manifests/site.pp
|
||||
puppet_modules: /etc/puppet/modules
|
||||
timeout: 3600
|
|
@ -17,7 +17,7 @@
|
|||
"""
|
||||
Release object and collection
|
||||
"""
|
||||
|
||||
from distutils.version import StrictVersion
|
||||
from sqlalchemy import not_
|
||||
import yaml
|
||||
|
||||
|
@ -149,6 +149,16 @@ class Release(NailgunObject):
|
|||
return True
|
||||
return instance.is_deployable
|
||||
|
||||
@classmethod
|
||||
def is_granular_enabled(cls, instance):
|
||||
"""Check if granular deployment is available for release
|
||||
|
||||
:param instance: a Release instance
|
||||
:returns: boolean
|
||||
"""
|
||||
return (StrictVersion(instance.fuel_version) >=
|
||||
StrictVersion(consts.FUEL_GRANULAR_DEPLOY))
|
||||
|
||||
@classmethod
|
||||
def get_deployment_tasks(cls, instance):
|
||||
"""Get deployment graph based on release version."""
|
||||
|
@ -157,8 +167,8 @@ class Release(NailgunObject):
|
|||
return instance.deployment_tasks
|
||||
elif env_version.startswith('5.0'):
|
||||
return yaml.load(graph_configuration.DEPLOYMENT_50)
|
||||
else:
|
||||
return yaml.load(graph_configuration.DEPLOYMENT_CURRENT)
|
||||
elif env_version.startswith('5.1') or env_version.startswith('6.0'):
|
||||
return yaml.load(graph_configuration.DEPLOYMENT_51_60)
|
||||
|
||||
|
||||
class ReleaseCollection(NailgunCollection):
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#(dshulyak) temporary, this config will be moved to fuel-library
|
||||
#until we will stabilize our api
|
||||
DEPLOYMENT_CURRENT = """
|
||||
DEPLOYMENT_51_60 = """
|
||||
|
||||
- id: deploy_start
|
||||
type: stage
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
# under the License.
|
||||
|
||||
from copy import deepcopy
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
import netaddr
|
||||
import requests
|
||||
|
@ -125,10 +124,9 @@ class DeploymentTask(object):
|
|||
:param cluster: Cluster db object
|
||||
:returns: string - deploy/granular_deploy
|
||||
"""
|
||||
if (StrictVersion(cluster.release.fuel_version) <
|
||||
StrictVersion(consts.FUEL_GRANULAR_DEPLOY)):
|
||||
return 'deploy'
|
||||
return 'granular_deploy'
|
||||
if objects.Release.is_granular_enabled(cluster.release):
|
||||
return 'granular_deploy'
|
||||
return 'deploy'
|
||||
|
||||
@classmethod
|
||||
def message(cls, task, nodes, deployment_tasks=None):
|
||||
|
|
|
@ -49,6 +49,7 @@ from nailgun.db import syncdb
|
|||
|
||||
from nailgun.logger import logger
|
||||
|
||||
from nailgun.db.sqlalchemy.fixman import load_fake_deployment_tasks
|
||||
from nailgun.db.sqlalchemy.fixman import load_fixture
|
||||
from nailgun.db.sqlalchemy.fixman import upload_fixture
|
||||
from nailgun.db.sqlalchemy.models import NodeAttributes
|
||||
|
@ -150,8 +151,11 @@ class EnvironmentManager(object):
|
|||
'roles': self.get_default_roles(),
|
||||
})
|
||||
|
||||
if kwargs:
|
||||
release_data.update(kwargs)
|
||||
if kwargs.get('deployment_tasks') is None:
|
||||
kwargs['deployment_tasks'] = \
|
||||
load_fake_deployment_tasks(apply_to_db=False)
|
||||
|
||||
release_data.update(kwargs)
|
||||
if api:
|
||||
resp = self.app.post(
|
||||
reverse('ReleaseCollectionHandler'),
|
||||
|
|
|
@ -1639,6 +1639,25 @@ class TestHandlers(BaseIntegrationTest):
|
|||
self.assertEqual(resp.status_code, 400)
|
||||
self.assertRegexpMatches(resp.body, 'Release .* is unavailable')
|
||||
|
||||
def test_occurs_error_no_deployment_tasks_for_release(self):
|
||||
self.env.create(
|
||||
nodes_kwargs=[
|
||||
{'roles': ['controller'], 'pending_addition': True}],
|
||||
release_kwargs={
|
||||
'version': "2014.2.2-6.1",
|
||||
'deployment_tasks': [],
|
||||
},
|
||||
)
|
||||
resp = self.app.put(
|
||||
reverse(
|
||||
'ClusterChangesHandler',
|
||||
kwargs={'cluster_id': self.env.clusters[0].id}),
|
||||
headers=self.default_headers,
|
||||
expect_errors=True)
|
||||
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
self.assertIn("Deployment tasks not found", resp.body)
|
||||
|
||||
@fake_tasks(override_state={"progress": 100, "status": "ready"})
|
||||
def test_enough_osds_for_ceph(self):
|
||||
cluster = self.env.create(
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
# under the License.
|
||||
|
||||
import cStringIO
|
||||
import os
|
||||
from oslo.serialization import jsonutils
|
||||
import yaml
|
||||
|
||||
from nailgun.db.sqlalchemy.fixman import upload_fixture
|
||||
from nailgun.db.sqlalchemy import fixman
|
||||
from nailgun.db.sqlalchemy.models import Node
|
||||
from nailgun.db.sqlalchemy.models import Release
|
||||
from nailgun.test.base import BaseIntegrationTest
|
||||
|
@ -32,6 +33,17 @@ class TestFixture(BaseIntegrationTest):
|
|||
check = self.db.query(Node).all()
|
||||
self.assertEqual(len(list(check)), 8)
|
||||
|
||||
def test_load_fake_deployment_tasks(self):
|
||||
fxtr_path = os.path.join(fixman.get_base_fixtures_path(),
|
||||
'deployment_tasks.yaml')
|
||||
with open(fxtr_path) as f:
|
||||
deployment_tasks = yaml.load(f)
|
||||
|
||||
fixman.load_fake_deployment_tasks()
|
||||
|
||||
for rel in self.db.query(Release).all():
|
||||
self.assertEqual(rel.deployment_tasks, deployment_tasks)
|
||||
|
||||
def test_json_fixture(self):
|
||||
data = '''[{
|
||||
"pk": 2,
|
||||
|
@ -44,7 +56,7 @@ class TestFixture(BaseIntegrationTest):
|
|||
}
|
||||
}]'''
|
||||
|
||||
upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
fixman.upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
check = self.db.query(Release).filter(
|
||||
Release.name == u"JSONFixtureRelease"
|
||||
)
|
||||
|
@ -66,7 +78,7 @@ class TestFixture(BaseIntegrationTest):
|
|||
operating_system: CentOS
|
||||
'''
|
||||
|
||||
upload_fixture(cStringIO.StringIO(data), loader=yaml)
|
||||
fixman.upload_fixture(cStringIO.StringIO(data), loader=yaml)
|
||||
check = self.db.query(Release).filter(
|
||||
Release.name == u"YAMLFixtureRelease"
|
||||
)
|
||||
|
@ -88,7 +100,7 @@ class TestFixture(BaseIntegrationTest):
|
|||
"roles": ["controller", "compute", "cinder", "ceph-osd"]
|
||||
}
|
||||
}]'''
|
||||
upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
fixman.upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
rel = self.db.query(Release).filter(
|
||||
Release.name == u"CustomFixtureRelease1"
|
||||
).all()
|
||||
|
@ -107,7 +119,7 @@ class TestFixture(BaseIntegrationTest):
|
|||
"roles": ["compute", "ceph-osd", "controller", "cinder"]
|
||||
}
|
||||
}]'''
|
||||
upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
fixman.upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
rel = self.db.query(Release).filter(
|
||||
Release.name == u"CustomFixtureRelease2"
|
||||
).all()
|
||||
|
@ -126,7 +138,7 @@ class TestFixture(BaseIntegrationTest):
|
|||
"roles": ["compute", "cinder", "controller", "cinder"]
|
||||
}
|
||||
}]'''
|
||||
upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
fixman.upload_fixture(cStringIO.StringIO(data), loader=jsonutils)
|
||||
rel = self.db.query(Release).filter(
|
||||
Release.name == u"CustomFixtureRelease3"
|
||||
).all()
|
||||
|
|
|
@ -290,6 +290,33 @@ class TestSelectedNodesAction(BaseSelectedNodesTest):
|
|||
"[{0}] marked for deletion".format(marked_for_deletion.id),
|
||||
resp.body)
|
||||
|
||||
@fake_tasks(fake_rpc=False, mock_rpc=False)
|
||||
@patch('nailgun.task.task.rpc.cast')
|
||||
def test_deployment_of_node_no_deployment_tasks(self, mcast):
|
||||
controller_nodes = [
|
||||
n for n in self.cluster.nodes
|
||||
if "controller" in n.roles
|
||||
]
|
||||
|
||||
self.emulate_nodes_provisioning(controller_nodes)
|
||||
|
||||
node_to_deploy = self.cluster.nodes[0]
|
||||
|
||||
deploy_action_url = self.make_action_url(
|
||||
"DeploySelectedNodes",
|
||||
[node_to_deploy.uid]
|
||||
)
|
||||
# overwriting default made in EnvironmentManager
|
||||
self.cluster.release.deployment_tasks = []
|
||||
|
||||
resp = self.send_put(deploy_action_url)
|
||||
resp_msg = jsonutils.loads(resp.body)['message']
|
||||
|
||||
self.assertFalse(mcast.called)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
self.assertIn("Deployment tasks not found", resp_msg)
|
||||
self.assertIn(self.cluster.release.name, resp_msg)
|
||||
|
||||
|
||||
class TestDeploymentHandlerSkipTasks(BaseSelectedNodesTest):
|
||||
|
||||
|
@ -353,7 +380,6 @@ class TestDeployMethodVersioning(BaseSelectedNodesTest):
|
|||
self.node_uids
|
||||
)
|
||||
self.send_put(action_url)
|
||||
|
||||
deployment_method = mcast.call_args_list[0][0][1]['method']
|
||||
self.assertEqual(deployment_method, method)
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ class TestLegacyGraphSerialized(base.BaseTestCase):
|
|||
super(TestLegacyGraphSerialized, self).setUp()
|
||||
self.cluster = mock.Mock()
|
||||
self.cluster.deployment_tasks = yaml.load(
|
||||
graph_configuration.DEPLOYMENT_CURRENT)
|
||||
graph_configuration.DEPLOYMENT_51_60)
|
||||
self.graph = deployment_graph.AstuteGraph(self.cluster)
|
||||
|
||||
def test_serialized_with_tasks_and_priorities(self):
|
||||
|
|
Loading…
Reference in New Issue