444 lines
17 KiB
Python
444 lines
17 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2015 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from copy import deepcopy
|
|
from mock import patch
|
|
|
|
from nailgun.api.v1.validators.cluster import VmwareAttributesValidator
|
|
from nailgun import consts
|
|
from nailgun import errors
|
|
from nailgun import objects
|
|
from nailgun.test.base import BaseTestCase
|
|
|
|
|
|
class TestAttributesValidator(BaseTestCase):
|
|
def setUp(self):
|
|
super(TestAttributesValidator, self).setUp()
|
|
self.cluster = self.env.create(
|
|
cluster_kwargs={
|
|
"api": False,
|
|
"vmware_attributes": {
|
|
"editable": self._get_value_vmware_attributes()
|
|
}
|
|
},
|
|
nodes_kwargs=[{
|
|
"hostname": "controller-node",
|
|
"status": consts.NODE_STATUSES.ready
|
|
}]
|
|
)
|
|
self.ready_compute_node = self.env.create_node(
|
|
hostname="node-1",
|
|
name="Node 1",
|
|
roles=["compute-vmware"],
|
|
status=consts.NODE_STATUSES.ready,
|
|
cluster_id=self.cluster.id
|
|
)
|
|
|
|
def _get_target_id(self, nova_compute):
|
|
return nova_compute["target_node"]["current"]["id"]
|
|
|
|
def _get_default_nova_computes(self):
|
|
return [
|
|
{
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster1",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "node-1",
|
|
"label": "node-1"
|
|
}
|
|
},
|
|
"service_name": "ns1"
|
|
},
|
|
{
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster0",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "controllers",
|
|
"label": "controllers"
|
|
}
|
|
},
|
|
"service_name": "ns0"
|
|
}
|
|
]
|
|
|
|
def _get_value_vmware_attributes(self, nova_computes=None):
|
|
return {
|
|
"value": {
|
|
"availability_zones": [{
|
|
"vcenter_username": "admin",
|
|
"az_name": "vcenter",
|
|
"vcenter_password": "pass",
|
|
"vcenter_host": "172.16.0.254",
|
|
"nova_computes":
|
|
nova_computes or self._get_default_nova_computes()
|
|
}]
|
|
}
|
|
}
|
|
|
|
def validate_nova_compute_raises_regexp(self, nova_computes, error_msg):
|
|
with self.assertRaisesRegexp(errors.InvalidData, error_msg):
|
|
VmwareAttributesValidator._validate_nova_computes(
|
|
{"editable": self._get_value_vmware_attributes(nova_computes)},
|
|
self.cluster.vmware_attributes
|
|
)
|
|
|
|
def test_change_exist_nova_compute(self):
|
|
nova_computes = self._get_default_nova_computes()
|
|
changed_attribute = 'vsphere_cluster'
|
|
for nc in nova_computes:
|
|
if self._get_target_id(nc) == self.ready_compute_node.hostname:
|
|
nc[changed_attribute] = "ClusterXX"
|
|
break
|
|
|
|
self.validate_nova_compute_raises_regexp(
|
|
nova_computes,
|
|
"Parameter '{0}' of nova compute instance with target node '{1}' "
|
|
"couldn't be changed".format(
|
|
changed_attribute, self.ready_compute_node.name
|
|
)
|
|
)
|
|
|
|
def test_delete_operational_nova_compute_node(self):
|
|
nova_computes = [
|
|
nc for nc in self._get_default_nova_computes() if
|
|
self._get_target_id(nc) != self.ready_compute_node.hostname
|
|
]
|
|
self.validate_nova_compute_raises_regexp(
|
|
nova_computes,
|
|
"The following compute-vmware node couldn't be deleted from "
|
|
"vSphere cluster: {0}".format(self.ready_compute_node.name)
|
|
)
|
|
|
|
def test_duplicate_values_for_nova_computes(self):
|
|
for attr in ("vsphere_cluster", "target_node", "service_name"):
|
|
exist_nova_computes = self._get_default_nova_computes()
|
|
duplicate_value = exist_nova_computes[0][attr]
|
|
new_nova_compute = {
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "ClusterXX",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "node-X",
|
|
"label": "node-X"
|
|
},
|
|
},
|
|
"service_name": "nsXX"
|
|
}
|
|
new_nova_compute.update({attr: duplicate_value})
|
|
exist_nova_computes.append(new_nova_compute)
|
|
duplicate_value = duplicate_value if attr != "target_node" \
|
|
else duplicate_value["current"]["id"]
|
|
|
|
self.validate_nova_compute_raises_regexp(
|
|
exist_nova_computes,
|
|
"Duplicate value '{0}' for attribute '{1}' is not allowed".
|
|
format(duplicate_value, attr)
|
|
)
|
|
|
|
def test_empty_values_for_nova_computes(self):
|
|
nova_computes = self._get_default_nova_computes()
|
|
nova_computes[0]["vsphere_cluster"] = ""
|
|
self.validate_nova_compute_raises_regexp(
|
|
nova_computes,
|
|
"Empty value for attribute 'vsphere_cluster' is not allowed"
|
|
)
|
|
|
|
def test_nova_compute_setting_validate_pass(self):
|
|
new_compute_vmware_node = self.env.create_node(
|
|
hostname="node-2",
|
|
name="Node 2",
|
|
pending_roles=["compute-vmware"],
|
|
pending_addition=True,
|
|
cluster_id=self.cluster.id
|
|
)
|
|
self.env.create_node(
|
|
hostname="node-3",
|
|
name="Node 3",
|
|
roles=["compute-vmware"],
|
|
pending_deletion=True,
|
|
cluster_id=self.cluster.id
|
|
)
|
|
attributes = self.cluster.vmware_attributes
|
|
new_nova_computes = self._get_default_nova_computes()
|
|
new_nova_computes.extend([{
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster2",
|
|
"target_node": {
|
|
"current": {
|
|
"id": new_compute_vmware_node.hostname,
|
|
"label": new_compute_vmware_node.name
|
|
}
|
|
},
|
|
"service_name": "ns2"
|
|
}])
|
|
self.assertNotRaises(errors.InvalidData,
|
|
VmwareAttributesValidator._validate_nova_computes,
|
|
{"editable": self._get_value_vmware_attributes(
|
|
new_nova_computes)},
|
|
attributes)
|
|
|
|
def test_change_controller_nova_computes_pass(self):
|
|
cluster = self.env.create(
|
|
cluster_kwargs={
|
|
"api": False,
|
|
"status": consts.CLUSTER_STATUSES.new,
|
|
"vmware_attributes": {
|
|
"editable": self._get_value_vmware_attributes()
|
|
}
|
|
}
|
|
)
|
|
new_nova_computes = [nc for nc in self._get_default_nova_computes()
|
|
if self._get_target_id(nc) == "controllers"]
|
|
new_nova_computes[0]["vsphere_cluster"] = "new vsphere name"
|
|
new_nova_computes.append({
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster10",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "controllers",
|
|
"label": "controllers"
|
|
}
|
|
},
|
|
"service_name": "ns10"
|
|
})
|
|
self.assertNotRaises(errors.InvalidData,
|
|
VmwareAttributesValidator._validate_nova_computes,
|
|
{"editable": self._get_value_vmware_attributes(
|
|
new_nova_computes)},
|
|
cluster.vmware_attributes)
|
|
|
|
@patch("nailgun.db.sqlalchemy.models.Cluster.is_locked", return_value=True)
|
|
def test_change_controllers_nova_compute_setting(self, lock_mock):
|
|
new_nova_computes = self._get_default_nova_computes()
|
|
changed_vsphere_cluster = None
|
|
changed_attribute = "service_name"
|
|
for nc in new_nova_computes:
|
|
if self._get_target_id(nc) == "controllers":
|
|
nc[changed_attribute] = "new_service_name"
|
|
changed_vsphere_cluster = nc
|
|
break
|
|
|
|
self.validate_nova_compute_raises_regexp(
|
|
new_nova_computes,
|
|
"Parameter '{0}' of nova compute instance with vSphere cluster "
|
|
"name '{1}' couldn't be changed".format(
|
|
changed_attribute, changed_vsphere_cluster["vsphere_cluster"])
|
|
)
|
|
|
|
@patch("nailgun.db.sqlalchemy.models.Cluster.is_locked", return_value=True)
|
|
def test_add_controllers_nova_compute_setting(self, lock_mock):
|
|
new_nova_computes = self._get_default_nova_computes()
|
|
new_nova_computes.extend([{
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster20",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "controllers",
|
|
"label": "controllers"
|
|
}
|
|
},
|
|
"service_name": "ns20"
|
|
}, {
|
|
"datastore_regex": ".*",
|
|
"vsphere_cluster": "Cluster30",
|
|
"target_node": {
|
|
"current": {
|
|
"id": "controllers",
|
|
"label": "controllers"
|
|
}
|
|
},
|
|
"service_name": "ns30"
|
|
}])
|
|
self.validate_nova_compute_raises_regexp(
|
|
new_nova_computes,
|
|
"Nova compute instances with target 'controllers' couldn't be "
|
|
"added to operational environment. Check nova compute instances "
|
|
"with the following vSphere cluster names: {0}".format(
|
|
", ".join(sorted(["Cluster30", "Cluster20"]))
|
|
)
|
|
)
|
|
|
|
@patch("nailgun.db.sqlalchemy.models.Cluster.is_locked", return_value=True)
|
|
def test_remove_controllers_nova_compute_setting(self, lock_mock):
|
|
new_nova_computes = [nc for nc in self._get_default_nova_computes()
|
|
if self._get_target_id(nc) != "controllers"]
|
|
self.validate_nova_compute_raises_regexp(
|
|
new_nova_computes,
|
|
"Nova compute instance with target 'controllers' and vSphere "
|
|
"cluster {0} couldn't be deleted from operational environment."
|
|
.format("Cluster0")
|
|
)
|
|
|
|
def test_update_non_editable_attributes(self):
|
|
metadata = [
|
|
{
|
|
"name": "foo",
|
|
"label": "foo",
|
|
"type": "object",
|
|
"fields": [{
|
|
"name": "foo_field_name",
|
|
"label": "foo_field_name",
|
|
}]
|
|
}, {
|
|
"name": "availability_zones",
|
|
"label": "availability_zones",
|
|
"type": "array",
|
|
"fields": [{
|
|
"name": "az_name",
|
|
"label": "az_name",
|
|
}, {
|
|
"name": "nova_computes",
|
|
"type": "array",
|
|
"fields": [{
|
|
"name": "vsphere_cluster",
|
|
"label": "vsphere_cluster",
|
|
}, {
|
|
"name": "target_node",
|
|
"label": "target_node",
|
|
}]
|
|
}, {
|
|
"name": "vcenter_host",
|
|
"label": "vcenter_host",
|
|
}]
|
|
}
|
|
]
|
|
db_attributes_value = {
|
|
"availability_zones": [{
|
|
"az_name": "az_1",
|
|
"vcenter_host": "127.0.0.1",
|
|
"nova_computes": [{
|
|
"vsphere_cluster": "Cluster1",
|
|
"target_node": {
|
|
"current": {"id": "node-1"}
|
|
}
|
|
}]
|
|
}],
|
|
"foo": {
|
|
"foo_field_name": "foo_field_value"
|
|
}
|
|
}
|
|
instance = objects.VmwareAttributes.create(
|
|
{"editable": {"metadata": metadata, "value": db_attributes_value}}
|
|
)
|
|
|
|
new_attributes = deepcopy(db_attributes_value)
|
|
new_attributes["foo"] = ["foo_field_name"]
|
|
msg = "Value type of 'foo_field_name' attribute couldn't be changed."
|
|
with self.assertRaisesRegexp(errors.InvalidData, msg):
|
|
VmwareAttributesValidator._validate_updated_attributes(
|
|
{"editable": {"value": new_attributes}},
|
|
instance)
|
|
|
|
new_attributes = deepcopy(db_attributes_value)
|
|
new_attributes["foo"]["foo_field_name"] = "new_foo_field_value"
|
|
msg = "Value of 'foo_field_name' attribute couldn't be changed."
|
|
with self.assertRaisesRegexp(errors.InvalidData, msg):
|
|
VmwareAttributesValidator._validate_updated_attributes(
|
|
{"editable": {"value": new_attributes}},
|
|
instance)
|
|
|
|
new_attributes = deepcopy(db_attributes_value)
|
|
new_attributes["availability_zones"].append({
|
|
"az_name": "az_2",
|
|
"vcenter_host": "127.0.0.1",
|
|
"nova_computes": []
|
|
})
|
|
msg = "Value of 'availability_zones' attribute couldn't be changed."
|
|
with self.assertRaisesRegexp(errors.InvalidData, msg):
|
|
VmwareAttributesValidator._validate_updated_attributes(
|
|
{"editable": {"value": new_attributes}}, instance)
|
|
|
|
new_attributes = deepcopy(db_attributes_value)
|
|
new_attributes["availability_zones"][0]["nova_computes"][0].update(
|
|
{"target_node": {"current": {"id": "node-2"}}}
|
|
)
|
|
msg = "Value of 'target_node' attribute couldn't be changed."
|
|
with self.assertRaisesRegexp(errors.InvalidData, msg):
|
|
VmwareAttributesValidator._validate_updated_attributes(
|
|
{"editable": {"value": new_attributes}}, instance)
|
|
|
|
def test_update_editable_attributes(self):
|
|
metadata = [
|
|
{
|
|
"name": "foo",
|
|
"label": "foo",
|
|
"type": "object",
|
|
"editable_for_deployed": True,
|
|
"fields": [{
|
|
"name": "foo_field_name",
|
|
"label": "foo_field_name",
|
|
}]
|
|
}, {
|
|
"name": "availability_zones",
|
|
"type": "array",
|
|
"label": "availability_zones",
|
|
"fields": [{
|
|
"name": "az_name",
|
|
"label": "az_name",
|
|
}, {
|
|
"name": "nova_computes",
|
|
"editable_for_deployed": True,
|
|
"type": "array",
|
|
"fields": [{
|
|
"name": "vsphere_cluster",
|
|
"label": "vsphere_cluster",
|
|
}, {
|
|
"name": "target_node",
|
|
"label": "target_node",
|
|
}]
|
|
}, {
|
|
"name": "vcenter_host",
|
|
"label": "vcenter_host",
|
|
}]
|
|
}
|
|
]
|
|
db_attributes_value = {
|
|
"availability_zones": [{
|
|
"az_name": "az_1",
|
|
"vcenter_host": "127.0.0.1",
|
|
"nova_computes": [{
|
|
"vsphere_cluster": "Cluster1",
|
|
"target_node": {
|
|
"current": {"id": "node-1"}
|
|
}
|
|
}]
|
|
}],
|
|
"foo": {
|
|
"foo_field_name": "foo_field_value"
|
|
}
|
|
}
|
|
instance = objects.VmwareAttributes.create(
|
|
{"editable": {"metadata": metadata, "value": db_attributes_value}}
|
|
)
|
|
|
|
new_attributes = deepcopy(db_attributes_value)
|
|
new_attributes["foo"]["foo_field_name"] = 1
|
|
new_attributes["availability_zones"][0]["nova_computes"][0].update(
|
|
{"target_node": {"current": {"id": "node-2"}}}
|
|
)
|
|
new_attributes["availability_zones"][0]["nova_computes"].append({
|
|
"vsphere_cluster": "Cluster2",
|
|
"target_node": {
|
|
"current": {"id": "node-2"}
|
|
}
|
|
})
|
|
self.assertNotRaises(
|
|
errors.InvalidData,
|
|
VmwareAttributesValidator._validate_updated_attributes,
|
|
{"editable": {"value": new_attributes}},
|
|
instance)
|