def test_clean_disabled_boot_arches_hex(self): subnet = factory.make_Subnet() disabled_arches = random.sample( [ boot_method for _, boot_method in BootMethodRegistry if boot_method.arch_octet ], 3, ) form = SubnetForm( instance=subnet, data={ "disabled_boot_architectures": ",".join([ bm.arch_octet.replace("00:", "0x") for bm in disabled_arches ]) }, ) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertEqual( sorted([bm.name for bm in disabled_arches]), sorted(subnet.disabled_boot_architectures), )
def test__creates_subnet(self): subnet_name = factory.make_name("subnet") subnet_description = factory.make_name("description") vlan = factory.make_VLAN() network = factory.make_ip4_or_6_network() cidr = str(network.cidr) gateway_ip = factory.pick_ip_in_network(network) dns_servers = [] for _ in range(2): dns_servers.append( factory.pick_ip_in_network(network, but_not=[gateway_ip] + dns_servers)) form = SubnetForm({ "name": subnet_name, "description": subnet_description, "vlan": vlan.id, "cidr": cidr, "gateway_ip": gateway_ip, "dns_servers": ",".join(dns_servers), }) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality( name=subnet_name, description=subnet_description, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip, dns_servers=dns_servers, ), )
def test__updates_subnet(self): new_name = factory.make_name("subnet") new_description = factory.make_name("description") subnet = factory.make_Subnet() new_vlan = factory.make_VLAN() new_network = factory.make_ip4_or_6_network() new_cidr = str(new_network.cidr) new_gateway_ip = factory.pick_ip_in_network(new_network) new_dns_servers = [] for _ in range(2): new_dns_servers.append( factory.pick_ip_in_network(new_network, but_not=[new_gateway_ip] + new_dns_servers)) form = SubnetForm(instance=subnet, data={ "name": new_name, "description": new_description, "vlan": new_vlan.id, "cidr": new_cidr, "gateway_ip": new_gateway_ip, "dns_servers": ','.join(new_dns_servers), }) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertThat( subnet, MatchesStructure.byEquality(name=new_name, description=new_description, vlan=new_vlan, cidr=new_cidr, gateway_ip=new_gateway_ip, dns_servers=new_dns_servers))
def test__removes_host_bits_and_whitespace(self): form = SubnetForm({ "cidr": ' 10.0.0.1/24 ', }) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat(subnet.cidr, Equals('10.0.0.0/24'))
def test__rejects_ipv6_cidr_with_zero_prefixlen(self): form = SubnetForm({"cidr": "::/0"}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( {"cidr": ["Prefix length must be greater than 0."]}, dict(form.errors), )
def test__rejects_invalid_cidr(self): form = SubnetForm( {"cidr": "ten dot zero dot zero dot zero slash zero"}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( {"cidr": ["Required format: <network>/<prefixlen>."]}, dict(form.errors), )
def test__clean_dns_servers_accepts_space_separated_list(self): subnet = factory.make_Subnet() dns_servers = [ factory.make_ip_address() for _ in range(random.randint(2, 10)) ] form = SubnetForm(instance=subnet, data={"dns_servers": " ".join(dns_servers)}) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertEquals(dns_servers, subnet.dns_servers)
def test__error_for_unknown_vid_in_default_fabric(self): fabric = factory.make_Fabric() vlan = factory.make_VLAN(fabric=fabric) network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"cidr": cidr, "vid": vlan.vid}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( {"vid": ["No VLAN with vid %s in default fabric." % vlan.vid]}, dict(form.errors), )
def test__error_for_vlan_not_in_fabric(self): fabric = factory.make_Fabric() vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric()) network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"cidr": cidr, "fabric": fabric.id, "vlan": vlan.id}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( {"vlan": ["VLAN %s is not in fabric %s." % (vlan, fabric)]}, dict(form.errors), )
def test_clean_disabled_detects_invalid_arch(self): subnet = factory.make_Subnet(disabled_boot_architectures=[]) form = SubnetForm( instance=subnet, data={ "disabled_boot_architectures": factory.make_name("boot_arch") }, ) self.assertFalse(form.is_valid()) subnet = reload_object(subnet) self.assertEqual([], subnet.disabled_boot_architectures)
def test__creates_subnet_in_default_fabric_with_vid(self): vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric()) network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"cidr": cidr, "vid": vlan.vid, "vlan": None}) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality(name=cidr, cidr=cidr, vlan=vlan), )
def test__creates_subnet_name_equal_to_cidr(self): vlan = factory.make_VLAN() network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"vlan": vlan.id, "cidr": cidr}) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality(name=cidr, vlan=vlan, cidr=cidr), )
def test__rejects_provided_space_on_update(self): space = factory.make_Space() subnet = factory.make_Subnet() form = SubnetForm(instance=subnet, data={"space": space.id}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( { "space": [ "Spaces may no longer be set on subnets. Set the space on the " "underlying VLAN." ] }, dict(form.errors))
def test__updates_subnet_name_doesnt_remove_dns_server(self): # Regression test for lp:1521833 dns_servers = [ factory.make_ip_address() for _ in range(random.randint(2, 10)) ] subnet = factory.make_Subnet(dns_servers=dns_servers) form = SubnetForm(instance=subnet, data={"name": factory.make_name("subnet")}) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertEquals(dns_servers, subnet.dns_servers)
def test_clears_gateway_and_dns_ervers(self): subnet = factory.make_Subnet() form = SubnetForm( instance=subnet, data={"gateway_ip": "", "dns_servers": ""} ) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertThat( subnet, MatchesStructure.byEquality(gateway_ip=None, dns_servers=[]), )
def update(self, request, id): """\ Update the specified subnet. Please see the documentation for the 'create' operation for detailed descriptions of each parameter. Optional parameters ------------------- name Name of the subnet. description Description of the subnet. vlan VLAN this subnet belongs to. space Space this subnet is in. cidr The network CIDR for this subnet. gateway_ip The gateway IP address for this subnet. rdns_mode How reverse DNS is handled for this subnet. allow_dns Configure MAAS DNS to allow DNS resolution from this subnet. allow_proxy Configure maas-proxy to allow requests from this subnet. dns_servers Comma-seperated list of DNS servers for this subnet. managed If False, MAAS should not manage this subnet. (Default: True) Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404(id, request.user, NodePermission.admin) form = SubnetForm(instance=subnet, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors)
def test__creates_subnet_in_default_vlan_in_fabric(self): fabric = factory.make_Fabric() network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"cidr": cidr, "fabric": fabric.id, "vlan": None}) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality(name=cidr, cidr=cidr, vlan=fabric.get_default_vlan()), )
def test__rejects_space_on_create(self): space = factory.make_Space() form = SubnetForm({ "space": space.id, "cidr": factory._make_random_network() }) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( { "space": [ "Spaces may no longer be set on subnets. Set the space on the " "underlying VLAN." ] }, dict(form.errors))
def test__creates_subnet_in_default_fabric_and_vlan(self): network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({"cidr": cidr}) self.assertTrue(form.is_valid(), dict(form.errors)) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality( name=cidr, cidr=cidr, vlan=Fabric.objects.get_default_fabric().get_default_vlan(), ), )
def test__doesnt_overwrite_other_fields(self): new_name = factory.make_name("subnet") subnet = factory.make_Subnet() form = SubnetForm(instance=subnet, data={ "name": new_name, }) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertThat( subnet, MatchesStructure.byEquality(name=new_name, vlan=subnet.vlan, cidr=subnet.cidr, gateway_ip=subnet.gateway_ip, dns_servers=subnet.dns_servers))
def test__error_for_unknown_vid_in_fabric(self): fabric = factory.make_Fabric() vlan = factory.make_VLAN(fabric=Fabric.objects.get_default_fabric()) network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({ "cidr": cidr, "fabric": fabric.id, "vid": vlan.vid, }) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual( { "vid": ["No VLAN with vid %s in fabric %s." % (vlan.vid, fabric)] }, dict(form.errors))
def test_clean_disabled_boot_arches_cannot_disable_fake_boot_arch(self): # The Windows boot loader responds to bootloader configuration requests # but isc-dhcpd is not configured to respond to a boot octet nor a # user-class. Thus it is unable to be disabled. subnet = factory.make_Subnet(disabled_boot_architectures=[]) choices = [ boot_method.name for _, boot_method in BootMethodRegistry if not boot_method.arch_octet and not boot_method.user_class ] form = SubnetForm( instance=subnet, data={"disabled_boot_architectures": random.choice(choices)}, ) self.assertFalse(form.is_valid()) subnet = reload_object(subnet) self.assertEqual([], subnet.disabled_boot_architectures)
def test__creates_subnet_in_fabric_with_vid(self): fabric = factory.make_Fabric() vlan = factory.make_VLAN(fabric=fabric) network = factory.make_ip4_or_6_network() cidr = str(network.cidr) form = SubnetForm({ "cidr": cidr, "fabric": fabric.id, "vid": vlan.vid, "vlan": None, }) self.assertTrue(form.is_valid(), form.errors) subnet = form.save() self.assertThat( subnet, MatchesStructure.byEquality(name=cidr, cidr=cidr, vlan=vlan))
def test_clean_disabled_boot_arches_name_space_seperated_list(self): subnet = factory.make_Subnet() disabled_arches = random.sample( [ boot_method.name for _, boot_method in BootMethodRegistry if boot_method.arch_octet or boot_method.user_class ], 3, ) form = SubnetForm( instance=subnet, data={"disabled_boot_architectures": " ".join(disabled_arches)}, ) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertEqual(sorted(disabled_arches), sorted(subnet.disabled_boot_architectures))
def test__updates_subnet_name_to_cidr(self): subnet = factory.make_Subnet() subnet.name = subnet.cidr subnet.save() new_network = factory.make_ip4_or_6_network() new_cidr = str(new_network.cidr) new_gateway_ip = factory.pick_ip_in_network(new_network) form = SubnetForm(instance=subnet, data={ "cidr": new_cidr, "gateway_ip": new_gateway_ip, }) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertThat( subnet, MatchesStructure.byEquality(name=new_cidr, cidr=new_cidr, gateway_ip=new_gateway_ip))
def create(self, request): """\ Create a subnet. Required parameters ------------------- cidr The network CIDR for this subnet. Optional parameters ------------------- name Name of the subnet. description Description of the subnet. vlan VLAN this subnet belongs to. Defaults to the default VLAN for the provided fabric or defaults to the default VLAN in the default fabric (if unspecified). fabric Fabric for the subnet. Defaults to the fabric the provided VLAN belongs to, or defaults to the default fabric. vid VID of the VLAN this subnet belongs to. Only used when vlan is not provided. Picks the VLAN with this VID in the provided fabric or the default fabric if one is not given. space Space this subnet is in. Defaults to the default space. gateway_ip The gateway IP address for this subnet. rdns_mode How reverse DNS is handled for this subnet. One of: 0 (Disabled), 1 (Enabled), or 2 (RFC2317). Disabled means no reverse zone is created; Enabled means generate the reverse zone; RFC2317 extends Enabled to create the necessary parent zone with the appropriate CNAME resource records for the network, if the network is small enough to require the support described in RFC2317. allow_dns Configure MAAS DNS to allow DNS resolution from this subnet. allow_proxy Configure maas-proxy to allow requests from this subnet. dns_servers Comma-seperated list of DNS servers for this subnet. managed In MAAS 2.0+, all subnets are assumed to be managed by default. Only managed subnets allow DHCP to be enabled on their related dynamic ranges. (Thus, dynamic ranges become "informational only"; an indication that another DHCP server is currently handling them, or that MAAS will handle them when the subnet is enabled for management.) Managed subnets do not allow IP allocation by default. The meaning of a "reserved" IP range is reversed for an unmanaged subnet. (That is, for managed subnets, "reserved" means "MAAS cannot allocate any IP address within this reserved block". For unmanaged subnets, "reserved" means "MAAS must allocate IP addresses only from reserved IP ranges". """ form = SubnetForm(data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors)
def create(self, request): """@description-title Create a subnet @description Creates a new subnet. @param (string) "cidr" [required=true] The network CIDR for this subnet. @param-example "cidr" 192.168.1.1/24 @param (string) "name" [required=false] The subnet's name. @param (string) "description" [required=false] The subnet's description. @param (string) "vlan" [required=false] VLAN this subnet belongs to. Defaults to the default VLAN for the provided fabric or defaults to the default VLAN in the default fabric (if unspecified). @param (string) "fabric" [required=false] Fabric for the subnet. Defaults to the fabric the provided VLAN belongs to, or defaults to the default fabric. @param (int) "vid" [required=false] VID of the VLAN this subnet belongs to. Only used when vlan is not provided. Picks the VLAN with this VID in the provided fabric or the default fabric if one is not given. @param (string) "space" [required=false] Space this subnet is in. Defaults to the default space. @param (string) "gateway_ip" [required=false] The gateway IP address for this subnet. @param (int) "rdns_mode" [required=false,formatting=true] How reverse DNS is handled for this subnet. One of: - ``0`` Disabled: No reverse zone is created. - ``1`` Enabled: Generate reverse zone. - ``2`` RFC2317: Extends '1' to create the necessary parent zone with the appropriate CNAME resource records for the network, if the the network is small enough to require the support described in RFC2317. @param (int) "allow_dns" [required=false] Configure MAAS DNS to allow DNS resolution from this subnet. '0' == False,'1' == True. @param (int) "allow_proxy" [required=false] Configure maas-proxy to allow requests from this subnet. '0' == False, '1' == True. @param (string) "dns_servers" [required=false] Comma-seperated list of DNS servers for this subnet. @param (int) "managed" [required=false,formatting=true] In MAAS 2.0+, all subnets are assumed to be managed by default. @param (string) "disabled_boot_architectures" [required=false] A comma or space seperated list of boot architectures which will not be responded to by isc-dhcpd. Values may be the MAAS name for the boot architecture, the IANA hex value, or the isc-dhcpd octet. Only managed subnets allow DHCP to be enabled on their related dynamic ranges. (Thus, dynamic ranges become "informational only"; an indication that another DHCP server is currently handling them, or that MAAS will handle them when the subnet is enabled for management.) Managed subnets do not allow IP allocation by default. The meaning of a "reserved" IP range is reversed for an unmanaged subnet. (That is, for managed subnets, "reserved" means "MAAS cannot allocate any IP address within this reserved block". For unmanaged subnets, "reserved" means "MAAS must allocate IP addresses only from reserved IP ranges." @success (http-status-code) "server-success" 200 @success (json) "success-json" A JSON object containing information about the new subnet. @success-example "success-json" [exkey=subnets-create] placeholder text """ form = SubnetForm(data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors)
def test__requires_cidr(self): form = SubnetForm({}) self.assertFalse(form.is_valid(), dict(form.errors)) self.assertEqual({"cidr": ["This field is required."]}, dict(form.errors))
def test__doest_require_vlan_or_cidr_on_update(self): subnet = factory.make_Subnet() form = SubnetForm(instance=subnet, data={}) self.assertTrue(form.is_valid(), dict(form.errors))