Allow node interfaces update if it's in 'discover' or 'error' state

Node inferfaces update is allowed when node is in one of the following states:
'discover', 'error'. It's locked for other node states.
It's not locked on node creation, i.e. when node.nic_interfaces is empty yet.

Closes-Bug: #1425901
Closes-Bug: #1428776
Change-Id: Icc60c432296c28e89d71b1fd0dd864870553a467
This commit is contained in:
Aleksey Kasatkin 2015-04-02 14:05:40 +03:00
parent 2245429119
commit 661a4f9502
4 changed files with 94 additions and 2 deletions

View File

@ -246,6 +246,10 @@ class NetAssignmentValidator(BasicValidator):
"There is no node with ID '{0}' in DB".format(node['id']),
log_message=True
)
if objects.Node.interfaces_locked(db_node):
raise errors.InvalidData(
"Node '{0}': Interfaces configuration can't be changed after "
"or during deployment.".format(db_node.id))
interfaces = node['interfaces']
db_interfaces = db_node.nic_interfaces
network_group_ids = objects.Node.get_network_manager(

View File

@ -354,6 +354,17 @@ class Node(NailgunObject):
"node_id": instance.id
})
@classmethod
def interfaces_locked(cls, instance):
"""Returns true if interfaces update is not allowed.
It is not allowed during provision/deployment, after
successful provision/deployment.
"""
return instance.status not in (
consts.NODE_STATUSES.discover,
consts.NODE_STATUSES.error,
)
@classmethod
def update(cls, instance, data):
"""Update Node instance with specified parameters in DB.
@ -396,8 +407,15 @@ class Node(NailgunObject):
if new_meta:
instance.update_meta(new_meta)
# smarter check needed
cls.update_interfaces(instance)
# The call to update_interfaces will execute a select query for
# the current instance. This appears to overwrite the object in the
# current session and we lose the meta changes.
db().flush()
if cls.interfaces_locked(instance):
logger.info("Interfaces are locked for update on node %s",
instance.human_readable_name)
else:
cls.update_interfaces(instance)
cluster_changed = False
if "cluster_id" in data:

View File

@ -14,6 +14,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from copy import deepcopy
import six
from nailgun import consts
from nailgun import objects
@ -229,3 +232,43 @@ class TestHandlers(BaseIntegrationTest):
headers=self.default_headers)
self.assertEqual(node.ip, ipaddress)
def test_NIC_locking_on_update_by_agent(self):
lock_vs_status = {
consts.NODE_STATUSES.discover: False,
consts.NODE_STATUSES.error: False,
consts.NODE_STATUSES.provisioning: True,
consts.NODE_STATUSES.provisioned: True,
consts.NODE_STATUSES.deploying: True,
consts.NODE_STATUSES.ready: True}
meta = self.env.default_metadata()
self.env.set_interfaces_in_meta(meta, [
{'name': 'eth0', 'mac': '00:00:00:00:00:00', 'current_speed': 1,
'state': 'up'}])
self.env.create_node(api=True, meta=meta)
new_meta = deepcopy(meta)
node = self.env.nodes[0]
for status, lock in six.iteritems(lock_vs_status):
node.status = status
self.db.flush()
new_meta['interfaces'][0]['current_speed'] += 1
node_data = {'mac': node['mac'], 'meta': new_meta}
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(node_data),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
resp = self.app.get(
reverse('NodeNICsHandler', kwargs={'node_id': node['id']}),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
resp_nic = resp.json_body[0]
new_speed = new_meta['interfaces'][0]['current_speed']
old_speed = meta['interfaces'][0]['current_speed']
self.assertEqual(resp_nic['current_speed'],
old_speed if lock else new_speed)
meta['interfaces'][0]['current_speed'] = resp_nic['current_speed']

View File

@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import six
from nailgun import consts
from nailgun.openstack.common import jsonutils
from nailgun.test.base import BaseIntegrationTest
@ -82,6 +84,9 @@ class TestNodeCollectionNICsHandler(BaseIntegrationTest):
self.assertEquals(0, len(changes))
node_id = self.env.nodes[0].id
# Change node status to be able to update its interfaces
self.env.nodes[0].status = 'discover'
self.db.flush()
# Getting nics
resp = self.env.node_nics_get(node_id)
interfaces = jsonutils.loads(resp.body)
@ -94,6 +99,28 @@ class TestNodeCollectionNICsHandler(BaseIntegrationTest):
)
self.assertEquals(1, len(changes))
def test_interface_changes_locking(self):
lock_vs_status = {
consts.NODE_STATUSES.discover: False,
consts.NODE_STATUSES.error: False,
consts.NODE_STATUSES.provisioning: True,
consts.NODE_STATUSES.provisioned: True,
consts.NODE_STATUSES.deploying: True,
consts.NODE_STATUSES.ready: True}
self.env.create_node(
roles=['controller'],
)
node = self.env.nodes[0]
for status, lock in six.iteritems(lock_vs_status):
node.status = status
self.db.flush()
# Getting nics
resp = self.env.node_nics_get(node.id)
# Updating nics
resp = self.env.node_nics_put(
node.id, resp.json_body, expect_errors=lock)
self.assertEqual(resp.status_code, 400 if lock else 200)
class TestNodeCollectionNICsDefaultHandler(BaseIntegrationTest):