Merge ~ltrager/maas:lp1911825_2.9 into maas:2.9

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: 835dfd7ed5346a347510b7019b757edd43b7a679
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1911825_2.9
Merge into: maas:2.9
Diff against target: 99 lines (+33/-15)
2 files modified
src/maasserver/models/bmc.py (+25/-6)
src/maasserver/models/tests/test_bmc.py (+8/-9)
Reviewer Review Type Date Requested Status
Lee Trager (community) Approve
Review via email: mp+396626@code.launchpad.net

Commit message

LP: #1911825 - Don't set BMC.ip_address when power_address contains an FQDN

When a BMC is created or edited MAAS maps its IP address to a subnet which
is stored in the model. This allows MAAS to send power commands only to
rack controllers which are on the same subnet as the BMC. If no rack
controller is on the same subnet as the BMC the power command is sent to
all rack controllers. When an FQDN or hostname were given an exception was
raised as MAAS expected an IP address. The exception is now avoided so the
fallback code is used which allow for FQDN or hostnames in the
power_address.

Backport of 19a09e6

To post a comment you must log in.
Revision history for this message
Lee Trager (ltrager) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/models/bmc.py b/src/maasserver/models/bmc.py
index 95f3f1a..7093759 100644
--- a/src/maasserver/models/bmc.py
+++ b/src/maasserver/models/bmc.py
@@ -1,4 +1,4 @@
1# Copyright 2015-2020 Canonical Ltd. This software is licensed under the1# Copyright 2015-2021 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""BMC objects."""4"""BMC objects."""
@@ -29,6 +29,7 @@ from django.db.models import (
29)29)
30from django.db.models.query import QuerySet30from django.db.models.query import QuerySet
31from django.shortcuts import get_object_or_40431from django.shortcuts import get_object_or_404
32from netaddr import AddrFormatError, IPAddress
32import petname33import petname
33from twisted.internet.defer import inlineCallbacks34from twisted.internet.defer import inlineCallbacks
3435
@@ -326,10 +327,6 @@ class BMC(CleanSave, TimestampedModel):
326 parameters has changed."""327 parameters has changed."""
327 new_ip = BMC.extract_ip_address(self.power_type, self.power_parameters)328 new_ip = BMC.extract_ip_address(self.power_type, self.power_parameters)
328 current_ip = None if self.ip_address is None else self.ip_address.ip329 current_ip = None if self.ip_address is None else self.ip_address.ip
329 # Set the ip_address field. If we have a bracketed address, assume
330 # it's IPv6, and strip the brackets.
331 if new_ip and new_ip.startswith("[") and new_ip.endswith("]"):
332 new_ip = new_ip[1:-1]
333 if new_ip != current_ip:330 if new_ip != current_ip:
334 if not new_ip:331 if not new_ip:
335 self.ip_address = None332 self.ip_address = None
@@ -424,7 +421,29 @@ class BMC(CleanSave, TimestampedModel):
424 return None421 return None
425 match = re.match(extraction_pattern, field_value)422 match = re.match(extraction_pattern, field_value)
426 if match:423 if match:
427 return match.group("address")424 ip = match.group("address")
425 # If we have a bracketed address, assume it's IPv6, and strip the
426 # brackets.
427 if ip.startswith("[") and ip.endswith("]"):
428 ip = ip[1:-1]
429 if ip == "":
430 return ip
431 # self.clean() attempts to map the return value of this method to
432 # a subnet. If the user gives an FQDN or hostname the mapping fails
433 # when Subnet.objects.get_best_subnet_for_ip() is called and an
434 # exception is raised as an IP is expected. If the BMC does not
435 # have an IP address MAAS will fall back on sending power requests
436 # to all connected rack controllers.
437 try:
438 IPAddress(ip)
439 except AddrFormatError:
440 maaslog.info(
441 "BMC uses FQDN, power action will be sent to all "
442 "rack controllers"
443 )
444 return None
445 else:
446 return ip
428 # no match found - return None447 # no match found - return None
429 return None448 return None
430449
diff --git a/src/maasserver/models/tests/test_bmc.py b/src/maasserver/models/tests/test_bmc.py
index 76216e9..700bc80 100644
--- a/src/maasserver/models/tests/test_bmc.py
+++ b/src/maasserver/models/tests/test_bmc.py
@@ -1,4 +1,4 @@
1# Copyright 2016-2020 Canonical Ltd. This software is licensed under the1# Copyright 2016-2021 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test maasserver models."""4"""Test maasserver models."""
@@ -433,14 +433,6 @@ class TestBMC(MAASServerTestCase):
433 power_parameters = {"power_address": None}433 power_parameters = {"power_address": None}
434 self.assertEqual(None, BMC.extract_ip_address("hmc", power_parameters))434 self.assertEqual(None, BMC.extract_ip_address("hmc", power_parameters))
435435
436 def test_bmc_extract_ip_address_from_url(self):
437 power_parameters = {
438 "power_address": "protocol://somehost:8080/path/to/thing#tag"
439 }
440 self.assertEqual(
441 "somehost", BMC.extract_ip_address("virsh", power_parameters)
442 )
443
444 def test_bmc_extract_ip_address_from_url_blank_gives_none(self):436 def test_bmc_extract_ip_address_from_url_blank_gives_none(self):
445 self.assertEqual(None, BMC.extract_ip_address("virsh", None))437 self.assertEqual(None, BMC.extract_ip_address("virsh", None))
446 self.assertEqual(None, BMC.extract_ip_address("virsh", {}))438 self.assertEqual(None, BMC.extract_ip_address("virsh", {}))
@@ -459,6 +451,13 @@ class TestBMC(MAASServerTestCase):
459 power_parameters = {"power_address": "http://:8080/foo/#baz"}451 power_parameters = {"power_address": "http://:8080/foo/#baz"}
460 self.assertEqual("", BMC.extract_ip_address("virsh", power_parameters))452 self.assertEqual("", BMC.extract_ip_address("virsh", power_parameters))
461453
454 def test_bmc_extract_ip_address_with_fqdn_returns_none(self):
455 self.assertIsNone(
456 BMC.extract_ip_address(
457 "webhook", {"power_address": factory.make_url()}
458 )
459 )
460
462 def test_get_usable_rack_controllers_returns_empty_when_none(self):461 def test_get_usable_rack_controllers_returns_empty_when_none(self):
463 bmc = factory.make_BMC()462 bmc = factory.make_BMC()
464 self.assertThat(bmc.get_usable_rack_controllers(), HasLength(0))463 self.assertThat(bmc.get_usable_rack_controllers(), HasLength(0))

Subscribers

People subscribed via source and target branches