Add 'deployed_before' flag to cluster that forbids stop deployment

When cluster is successfuly deployed, that is, has 'operational' status,
'deployed_before' key is added to generated attributes of the cluster.
This flag then is used to determine whether stop action is allowed for
the cluster.

Conflicts:
	nailgun/nailgun/objects/cluster.py
	nailgun/nailgun/test/unit/test_objects.py

Change-Id: I8ecfca5819352ac27d7cec91e22f8006d8c3d8de
Closes-Bug: #1529691
(cherry picked from commit e969206448)
This commit is contained in:
Artem Roma 2016-01-28 12:58:09 +02:00 committed by Rodion Tikunov
parent 9e2823cb44
commit a961ebc251
14 changed files with 179 additions and 4 deletions

View File

@ -551,6 +551,7 @@ class DeferredTaskHandler(BaseHandler):
errors.NoDeploymentTasks,
errors.WrongNodeStatus,
errors.UnavailableRelease,
errors.CannotBeStopped,
) as exc:
raise self.http(400, exc.message)
except Exception as exc:

View File

@ -33,6 +33,7 @@ from nailgun.api.v1.handlers.base import content
from nailgun.api.v1.validators.cluster import AttributesValidator
from nailgun.api.v1.validators.cluster import ClusterChangesValidator
from nailgun.api.v1.validators.cluster import ClusterStopDeploymentValidator
from nailgun.api.v1.validators.cluster import ClusterValidator
from nailgun.api.v1.validators.cluster import VmwareAttributesValidator
@ -95,6 +96,7 @@ class ClusterStopDeploymentHandler(DeferredTaskHandler):
log_error = u"Error during execution of deployment " \
u"stopping task on environment '{env_id}': {error}"
task_manager = StopDeploymentTaskManager
validator = ClusterStopDeploymentValidator
class ClusterResetHandler(DeferredTaskHandler):

View File

@ -238,6 +238,19 @@ class ClusterChangesValidator(BaseDefferedTaskValidator):
ProvisionSelectedNodesValidator.validate_provision(None, cluster)
class ClusterStopDeploymentValidator(BaseDefferedTaskValidator):
@classmethod
def validate(cls, cluster):
super(ClusterStopDeploymentValidator, cls).validate(cluster)
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
if cluster.attributes.generated['deployed_before']['value']:
raise errors.CannotBeStopped()
class VmwareAttributesValidator(BasicValidator):
single_schema = cluster_schema.vmware_attributes_schema

View File

@ -41,6 +41,7 @@ default_messages = {
"NoDeploymentTasks": "Deployment tasks not found for specific release in the database",
"DeletionAlreadyStarted": "Environment removal already started",
"StopAlreadyRunning": "Stopping deployment already initiated",
"CannotBeStopped": "Stop action is forbidden for the cluster",
"FailedProvisioning": "Failed to start provisioning",
"WrongNodeStatus": "Wrong node status",
"NodeOffline": "Node is offline",

View File

@ -907,6 +907,8 @@
puppet:
manifests: "rsync://{settings.MASTER_IP}:/puppet/{cluster.release.version}/manifests/"
modules: "rsync://{settings.MASTER_IP}:/puppet/{cluster.release.version}/modules/"
deployed_before:
value: false
wizard_metadata:
Mode:
metadata:

View File

@ -18,6 +18,7 @@
Cluster-related objects and collections
"""
import copy
from sqlalchemy import or_
import yaml
@ -856,6 +857,29 @@ class Cluster(NailgunObject):
repos = instance.attributes.editable['repo_setup']['repos']['value']
return tuple(set([r['uri'] for r in repos]))
# FIXME(aroma): remove updating of 'deployed_before'
# when stop action is reworked. 'deployed_before'
# flag identifies whether stop action is allowed for the
# cluster. Please, refer to [1] for more details.
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
@classmethod
def set_deployed_before_flag(cls, instance, value):
"""Change value for before_deployed if needed
:param instance: nailgun.db.sqlalchemy.models.Cluster instance
:param value: new value for flag
:type value: bool
"""
if instance.attributes.generated['deployed_before']['value'] != value:
# TODO(aroma): remove unnecessary copying when enhancement
# of Mutable types will be introduced for corresponding
# fields of ORM models
generated_attrs = copy.deepcopy(instance.attributes.generated)
generated_attrs['deployed_before']['value'] = value
instance.attributes.generated = generated_attrs
db.flush()
class ClusterCollection(NailgunCollection):
"""Cluster collection

View File

@ -183,7 +183,18 @@ class Task(NailgunObject):
logger.debug(
"Updating cluster (%s) status: from %s to %s",
cluster.full_name, cluster.status, status)
Cluster.update(cluster, data={'status': status})
data = {'status': status}
# FIXME(aroma): remove updating of 'deployed_before'
# when stop action is reworked. 'deployed_before'
# flag identifies whether stop action is allowed for the
# cluster. Please, refer to [1] for more details.
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
if status == consts.CLUSTER_STATUSES.operational:
Cluster.set_deployed_before_flag(cluster, value=True)
Cluster.update(cluster, data)
@classmethod
def _update_cluster_data(cls, instance):
@ -191,7 +202,7 @@ class Task(NailgunObject):
if instance.name == 'deploy':
if instance.status == 'ready':
# If for some reasosns orchestrator
# If for some reasons orchestrator
# didn't send ready status for node
# we should set it explicitly
for n in cluster.nodes:

View File

@ -627,6 +627,14 @@ class StopDeploymentTaskManager(TaskManager):
class ResetEnvironmentTaskManager(TaskManager):
def execute(self):
# FIXME(aroma): remove updating of 'deployed_before'
# when stop action is reworked. 'deployed_before'
# flag identifies whether stop action is allowed for the
# cluster. Please, refer to [1] for more details.
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
objects.Cluster.set_deployed_before_flag(self.cluster, value=False)
deploy_running = db().query(Task).filter_by(
cluster=self.cluster,
name=consts.TASK_NAMES.deploy,

View File

@ -58,6 +58,12 @@ class TestResetEnvironment(BaseIntegrationTest):
self.assertEqual(cluster_db.status, "new")
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
self.assertFalse(
cluster_db.attributes.generated['deployed_before']['value'])
for n in cluster_db.nodes:
self.assertEqual(n.online, False)
self.assertEqual(n.status, "discover")

View File

@ -25,6 +25,7 @@ from nailgun.db.sqlalchemy.models.task import Task
from nailgun.test.base import BaseIntegrationTest
from nailgun.test.base import fake_tasks
from nailgun.test.base import reverse
class TestStopDeployment(BaseIntegrationTest):
@ -78,6 +79,42 @@ class TestStopDeployment(BaseIntegrationTest):
notification.message,
'Please reset the environment if you want to redeploy it.')
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
@fake_tasks(tick_interval=1)
def test_stop_deployment_fail_if_deployed_before(self):
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
# changes to deploy
self.env.create_node(
cluster_id=self.cluster.id,
roles=["controller"],
pending_addition=True
)
redeploy_task = self.env.launch_deployment()
self.env.wait_ready(redeploy_task)
# stop task will not be created as in this situation
# the error will be raised by validator thus we cannot use
# self.env.stop_deployment to check the result
resp = self.app.put(
reverse(
'ClusterStopDeploymentHandler',
kwargs={'cluster_id': self.cluster.id}),
expect_errors=True,
headers=self.default_headers
)
self.assertEqual(resp.status_code, 400)
self.assertEqual(resp.json_body['message'],
'Stop action is forbidden for the cluster')
# wait that redeployment end successfully
self.env.wait_ready(redeploy_task)
@fake_tasks(fake_rpc=False, mock_rpc=False)
@patch('nailgun.rpc.cast')
def test_admin_ip_in_args(self, mocked_rpc):

View File

@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from mock import patch
from nailgun.test.base import BaseIntegrationTest
@ -158,6 +157,13 @@ class TestTasksLogging(BaseIntegrationTest):
]
)
self.env.launch_deployment()
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
cluster = self.env.clusters[0]
objects.Cluster.set_deployed_before_flag(cluster, value=False)
self.env.stop_deployment()
self.assertGreaterEqual(len(logger.call_args_list), 1)
@ -279,6 +285,12 @@ class TestTasksLogging(BaseIntegrationTest):
deploy_uuid = deploy.uuid
self.simulate_running_deployment(deploy)
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
cluster = self.env.clusters[0]
objects.Cluster.set_deployed_before_flag(cluster, value=False)
# Stopping deployment
self.env.stop_deployment()

View File

@ -21,6 +21,7 @@ from six.moves import range
import unittest2
from nailgun import consts
from nailgun import objects
from nailgun.test.base import BaseIntegrationTest
from nailgun.test.base import fake_tasks
from nailgun.utils import reverse
@ -523,6 +524,12 @@ class TestVerifyNeutronVlan(BaseIntegrationTest):
def test_verify_networks_after_stop(self):
self.cluster = self.env.clusters[0]
self.env.launch_deployment()
# FIXME(aroma): remove when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
objects.Cluster.set_deployed_before_flag(self.cluster, value=False)
stop_task = self.env.stop_deployment()
self.env.wait_ready(stop_task, 60)
self.assertEqual(self.cluster.status, consts.CLUSTER_STATUSES.stopped)

View File

@ -17,9 +17,11 @@ from mock import Mock
from mock import patch
from oslo.serialization import jsonutils
from nailgun.api.v1.validators.cluster import ClusterStopDeploymentValidator
from nailgun.api.v1.validators.cluster import ClusterValidator
from nailgun import consts
from nailgun.errors import errors
from nailgun import objects
from nailgun.test.base import BaseTestCase
@ -206,3 +208,20 @@ class TestClusterValidator(BaseTestCase):
ClusterValidator.validate_update,
self.cluster_data,
cluster_mock)
class TestClusterStopDeploymentValidator(BaseTestCase):
# FIXME(aroma): remove this test when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
def test_stop_deployment_failed_for_once_deployed_cluster(self):
cluster = self.env.create_cluster(api=False)
objects.Cluster.set_deployed_before_flag(cluster, value=True)
self.assertRaises(
errors.CannotBeStopped,
ClusterStopDeploymentValidator.validate,
cluster
)

View File

@ -18,6 +18,7 @@ import copy
import datetime
import hashlib
import jsonschema
import mock
import six
import uuid
@ -508,7 +509,10 @@ class TestTaskObject(BaseIntegrationTest):
objects.Task._update_cluster_data(task)
self.db.flush()
self.assertEquals(self.cluster.status, 'operational')
self.assertEqual(self.cluster.status,
consts.CLUSTER_STATUSES.operational)
self.assertTrue(
self.cluster.attributes.generated['deployed_before']['value'])
def test_update_if_parent_task_is_ready_all_nodes_should_be_ready(self):
for node in self.cluster.nodes:
@ -716,6 +720,34 @@ class TestClusterObject(BaseTestCase):
{'roles': ['compute']},
{'roles': ['cinder']}])
# FIXME(aroma): remove this test when stop action will be reworked for ha
# cluster. To get more details, please, refer to [1]
# [1]: https://bugs.launchpad.net/fuel/+bug/1529691
def test_set_deployed_before_flag(self):
self.cluster = self.env.clusters[0]
self.assertFalse(
self.cluster.attributes.generated['deployed_before']['value'])
# check that the flags is set to true if was false
objects.Cluster.set_deployed_before_flag(self.cluster, value=True)
self.assertTrue(
self.cluster.attributes.generated['deployed_before']['value'])
# check that flag is set to false if was true
objects.Cluster.set_deployed_before_flag(self.cluster, value=False)
self.assertFalse(
self.cluster.attributes.generated['deployed_before']['value'])
# check that flag is not changed when same value is given
# and interaction w/ db is not performed
with mock.patch.object(self.db, 'flush') as m_flush:
objects.Cluster.set_deployed_before_flag(self.cluster,
value=False)
self.assertFalse(
self.cluster.attributes.generated['deployed_before']['value'])
m_flush.assert_not_called()
def test_all_controllers(self):
self.assertEqual(len(objects.Cluster.get_nodes_by_role(
self.env.clusters[0], 'controller')), 2)