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:
parent
2245429119
commit
661a4f9502
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
Loading…
Reference in New Issue