Accept interfaces changes only in discovery/error

By our design we accept changes to node interfaces only if node in
DISCOVERY and ERROR states. There are few reasons why we're doing so,
including one that IP addresses are assigned to bridges, not to NICs
and we ignore it in order to be able to redeploy nodes.

However, there's one case when node status is *artificially* changing
to ERROR state, implicitly allowing to accept interfaces changes. The
case is:

* Admin (PXE) interface is down on a node.
* nailgun-agent sends wrong node's IP address that will be used to
  check whether it belongs to Admin (PXE) network or not.
* If it doesn't belong, change node status to ERROR.
* Since the status is ERROR, changes to interfaces are allowed.
* Move admin network to another interface, which is wrong since
  physical connection wasn't changed.

The commit introduces additional condition when the check for belonging
to Admin (PXE) network is allowed.

Change-Id: I17e87e27d846921d6f0da535b9446e716449db95
Closes-Bug: #1532823
This commit is contained in:
Igor Kalnitsky 2016-02-15 16:54:16 +02:00
parent 73f1df05f7
commit 5925c52524
2 changed files with 42 additions and 3 deletions

View File

@ -645,9 +645,8 @@ class Node(NailgunObject):
instance.human_readable_name)
meta['disks'] = instance.meta['disks']
# (dshulyak) change this verification to NODE_STATUSES.deploying
# after we will reuse ips from dhcp range
if data.get('ip'):
if not cls.is_interfaces_configuration_locked(instance) \
and data.get('ip'):
if instance.cluster_id:
update_status = cls.check_ip_belongs_to_own_admin_network(
instance, data['ip'])

View File

@ -368,3 +368,43 @@ class TestHandlers(BaseIntegrationTest):
'ip': ipaddress}),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
def test_do_not_update_interfaces_on_incorrect_ip(self):
self.env.create(
nodes_kwargs=[
{'api': False, 'status': consts.NODE_STATUSES.ready},
])
node = self.env.nodes[0]
node.interfaces[1].ip_addr = '172.16.0.2' # set public net ip
# get node representation
resp = self.app.get(
reverse('NodeHandler', kwargs={'obj_id': node.id}),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
node_json = resp.json
# deployed nodes have ip=null and pxe=false
for iface in node_json['meta']['interfaces']:
iface.pop('ip', None)
iface['pxe'] = False
# pick not-admin interface, and pass its ip & mac on node level
node_json.update({
'ip': node.interfaces[1].ip_addr,
'mac': node.interfaces[1].mac,
})
resp = self.app.put(
reverse('NodeAgentHandler'),
jsonutils.dumps(node_json),
headers=self.default_headers)
self.assertEqual(resp.status_code, 200)
# check that nothing is broken: admin network isn't jumped to
# another interface
self.assertIsNotNone(
next((net for net in node.interfaces[0].assigned_networks
if net['name'] == consts.NETWORKS.fuelweb_admin), None))
self.assertIsNone(
next((net for net in node.interfaces[1].assigned_networks
if net['name'] == consts.NETWORKS.fuelweb_admin), None))