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:
parent
1101502480
commit
db99490017
|
@ -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}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue