Networks configuration API is now available for stopped and ready nodes

So it is possible to update network configuration for node in
'stopped' and 'ready' state using NodeCollectionHandler,
NodeNICsHandler and NodeCollectionNICsHandler

Network config update via NodeAgentHandler is still restricted but
it is still possible to update metadata from this handler.

'is agent' request flag is now added automatically if not persist
when update is launched via NodeAgentHandler.

Change-Id: I5f41c74553b4227cd968900fb121d26ff2029cda
Partial-Bug: #1581527
This commit is contained in:
Jenkins 2016-05-24 10:01:56 +00:00 committed by Ilya Kutukov
parent 1101502480
commit db99490017
5 changed files with 101 additions and 11 deletions

View File

@ -192,7 +192,7 @@ class NodeAgentHandler(BaseHandler):
node.agent_checksum == nd['agent_checksum']
):
return {'id': node.id, 'cached': True}
nd['is_agent'] = True
self.collection.single.update_by_agent(node, nd)
return {"id": node.id}

View File

@ -530,15 +530,36 @@ class Node(NailgunObject):
db().flush()
@classmethod
def is_interfaces_configuration_locked(cls, instance):
def is_interfaces_configuration_locked(cls, instance, is_agent=False):
"""Returns true if update of network configuration is not allowed.
Update of network configuration is allowed for bootstrap nodes only.
Configuration update is allowed in 'discover', 'stopped',
'ready' and discovery 'error'.
We are allowing configuration of the 'stopped' and 'ready' node to be
updated by API but not nailgun-agent.
:param instance: node instance
:type instance: models.Node
:param is_agent: is nailgun-agent
:type is_agent: bool
:return: is locked
:rtype: bool
"""
return instance.status not in (
consts.NODE_STATUSES.discover,
consts.NODE_STATUSES.error,
) or (
if is_agent:
unlocked_cluster_statuses = (
consts.NODE_STATUSES.discover,
consts.NODE_STATUSES.error,
)
else:
unlocked_cluster_statuses = (
consts.NODE_STATUSES.discover,
consts.NODE_STATUSES.error,
consts.NODE_STATUSES.stopped,
consts.NODE_STATUSES.ready
)
return instance.status not in unlocked_cluster_statuses or (
instance.status == consts.NODE_STATUSES.error and
instance.error_type != consts.NODE_ERRORS.discover
)
@ -668,7 +689,8 @@ class Node(NailgunObject):
# the current instance. This appears to overwrite the object in the
# current session and we lose the meta changes.
db().flush()
if cls.is_interfaces_configuration_locked(instance):
is_agent = bool(data.pop('is_agent', None))
if cls.is_interfaces_configuration_locked(instance, is_agent):
logger.debug("Interfaces are locked for update on node %s",
instance.human_readable_name)
else:
@ -678,7 +700,7 @@ class Node(NailgunObject):
cls.update_interfaces(instance)
cls.update_interfaces_offloading_modes(
instance,
bool(data.pop('is_agent', None)))
is_agent)
cluster_changed = False
add_to_cluster = False
@ -846,7 +868,7 @@ class Node(NailgunObject):
instance.human_readable_name)
meta['disks'] = instance.meta['disks']
if not cls.is_interfaces_configuration_locked(instance) \
if not cls.is_interfaces_configuration_locked(instance, is_agent=True) \
and data.get('ip'):
if instance.cluster_id:
update_status = cls.check_ip_belongs_to_own_admin_network(

View File

@ -19,6 +19,7 @@ import copy
from oslo_serialization import jsonutils
from nailgun.db.sqlalchemy.models import Node
from nailgun.db.sqlalchemy.models import NodeNICInterface
from nailgun.db.sqlalchemy.models import Notification
from nailgun.test.base import BaseIntegrationTest
from nailgun.utils import reverse
@ -251,6 +252,71 @@ class TestHandlers(BaseIntegrationTest):
self.assertEqual('new', node_db.manufacturer)
self.assertEqual('provisioning', node_db.status)
def test_stopped_node_network_update_restricted_for_agent(self):
node = self.env.create_node(
api=False,
status='stopped',
meta=self.env.default_metadata()
)
node_db = self.env.nodes[0]
interfaces = node.meta['interfaces']
new_interfaces = copy.deepcopy(interfaces)
new_interfaces[1]['mac'] = '2a:00:0d:0d:00:2a'
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(
{
'mac': node_db.mac,
'meta': {
'interfaces': new_interfaces
}
}
),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
node_db = self.db.query(Node).get(node_db.id)
interface_db = self.db.query(NodeNICInterface).filter_by(
node_id=node_db.id,
name=new_interfaces[1]['name']
).first()
self.assertNotEqual(
interface_db.mac,
'2a:00:0d:0d:00:2a')
def test_stopped_node_network_update_allowed_for_ui(self):
node = self.env.create_node(
api=False,
status='stopped',
meta=self.env.default_metadata()
)
node_db = self.env.nodes[0]
interfaces = node.meta['interfaces']
new_interfaces = copy.deepcopy(interfaces)
new_interfaces[1]['mac'] = '2a:00:0d:0d:00:2a'
resp = self.app.put(
reverse('NodeCollectionHandler'),
jsonutils.dumps([
{
'mac': node_db.mac,
'meta': {
'interfaces': new_interfaces
}
}
]),
headers=self.default_headers
)
self.assertEqual(resp.status_code, 200)
interface_db = self.db.query(NodeNICInterface).filter_by(
node_id=node_db.id,
name=new_interfaces[1]['name']
).first()
self.assertEqual(
interface_db.mac,
'2a:00:0d:0d:00:2a')
def test_node_timestamp_updated_only_by_agent(self):
node = self.env.create_node(api=False)
timestamp = node.timestamp

View File

@ -131,7 +131,8 @@ class TestNodeCollectionNICsHandler(BaseIntegrationTest):
consts.NODE_STATUSES.provisioning: True,
consts.NODE_STATUSES.provisioned: True,
consts.NODE_STATUSES.deploying: True,
consts.NODE_STATUSES.ready: True,
consts.NODE_STATUSES.ready: False,
consts.NODE_STATUSES.stopped: False,
consts.NODE_STATUSES.removing: True}
meta = self.env.default_metadata()
meta['interfaces'] = [{'name': 'eth0', 'pxe': True},

View File

@ -512,6 +512,7 @@ class TestHandlers(BaseIntegrationTest):
consts.NODE_ERRORS.stop_deployment),
(consts.NODE_STATUSES.provisioning, True),
(consts.NODE_STATUSES.provisioned, True),
(consts.NODE_STATUSES.stopped, True),
(consts.NODE_STATUSES.deploying, True),
(consts.NODE_STATUSES.ready, True),
(consts.NODE_STATUSES.removing, True))