def save(self): """Save the form's data to the database. This implementation of `save` does not support the `commit` argument. """ node = super(WithMACAddressesMixin, self).save(commit=False) # We have to save this node in order to attach MACAddress # records to it. But its nodegroup must be initialized before # we can do that. # As a side effect, this prevents editing of the node group on # an existing node. It's all horribly dependent on the order of # calls in this class family, but Django doesn't seem to give us # a good way around it. initialize_node_group(node, self.cleaned_data.get('nodegroup')) node.save() for mac in self.cleaned_data['mac_addresses']: node.add_mac_address(mac) hostname = self.cleaned_data['hostname'] stripped_hostname = strip_domain(hostname) # Generate a hostname for this node if the provided hostname is # IP-based (because this means that this name comes from a DNS # reverse query to the MAAS DNS) or an empty string. generate_hostname = ( hostname == "" or IP_BASED_HOSTNAME_REGEXP.match(stripped_hostname) is not None) if generate_hostname: node.set_random_hostname() return node
def get_hostname_ip_mapping(self, nodegroup): """Return a mapping {hostnames -> ips} for the currently leased IP addresses for the nodes in `nodegroup`. This will consider only the first interface (i.e. the first MAC Address) associated with each node withing the given `nodegroup`. If the hostnames contain a domain, it gets removed. """ cursor = connection.cursor() # The subquery fetches the IDs of the first MAC Address for # all the nodes in this nodegroup. # Then the main query returns the hostname -> ip mapping for # these MAC Addresses. cursor.execute(""" SELECT node.hostname, lease.ip FROM maasserver_macaddress as mac, maasserver_node as node, maasserver_dhcplease as lease WHERE mac.id IN ( SELECT DISTINCT ON (node_id) mac.id FROM maasserver_macaddress as mac, maasserver_node as node WHERE node.nodegroup_id = %s AND mac.node_id = node.id ORDER BY node_id, mac.id ) AND mac.node_id = node.id AND mac.mac_address = lease.mac AND lease.nodegroup_id = %s """, (nodegroup.id, nodegroup.id)) return dict( (strip_domain(hostname), ip) for hostname, ip in cursor.fetchall() )
def get_hostname_ip_mapping(self, nodegroup): """Return a mapping {hostnames -> ips} for the currently leased IP addresses for the nodes in `nodegroup`. For each node, this will consider only the oldest `MACAddress` that has a `DHCPLease`. Any domain will be stripped from the hostnames. """ cursor = connection.cursor() # The "DISTINCT ON" gives us the first matching row for any # given hostname, in the query's ordering. # The ordering must start with the hostname so that the database # can do this efficiently. The next ordering criterion is the # MACAddress id, so that if there are multiple rows with the # same hostname, we get the one with the oldest MACAddress. # # If this turns out to be inefficient, be sure to try selecting # on node.nodegroup_id instead of lease.nodegroup_id. It has # the same effect but may perform differently. cursor.execute(""" SELECT DISTINCT ON (node.hostname) node.hostname, lease.ip FROM maasserver_macaddress AS mac JOIN maasserver_node AS node ON node.id = mac.node_id JOIN maasserver_dhcplease AS lease ON lease.mac = mac.mac_address WHERE lease.nodegroup_id = %s ORDER BY node.hostname, mac.id """, (nodegroup.id, )) return dict( (strip_domain(hostname), ip) for hostname, ip in cursor.fetchall() )
def get_hostname_ip_mapping(self, nodegroup): """Return a mapping {hostnames -> ips} for the currently leased IP addresses for the nodes in `nodegroup`. For each node, this will consider only the oldest `MACAddress` that has a `DHCPLease`. Any domain will be stripped from the hostnames. """ cursor = connection.cursor() # The "DISTINCT ON" gives us the first matching row for any # given hostname, in the query's ordering. # The ordering must start with the hostname so that the database # can do this efficiently. The next ordering criterion is the # MACAddress id, so that if there are multiple rows with the # same hostname, we get the one with the oldest MACAddress. # # If this turns out to be inefficient, be sure to try selecting # on node.nodegroup_id instead of lease.nodegroup_id. It has # the same effect but may perform differently. cursor.execute( """ SELECT DISTINCT ON (node.hostname) node.hostname, lease.ip FROM maasserver_macaddress AS mac JOIN maasserver_node AS node ON node.id = mac.node_id JOIN maasserver_dhcplease AS lease ON lease.mac = mac.mac_address WHERE lease.nodegroup_id = %s ORDER BY node.hostname, mac.id """, (nodegroup.id, )) return dict( (strip_domain(hostname), ip) for hostname, ip in cursor.fetchall())
def test_POST_create_with_no_hostname_auto_populates_hostname(self): architecture = make_usable_architecture(self) response = self.client.post( reverse('machines_handler'), { 'architecture': architecture, 'power_type': 'manual', 'mac_addresses': [factory.make_mac_address()], }) machine = Machine.objects.get( system_id=json_load_bytes(response.content)['system_id']) self.assertNotEqual("", strip_domain(machine.hostname))
def test_POST_with_no_hostname_auto_populates_hostname(self): architecture = factory.getRandomChoice(ARCHITECTURE_CHOICES) response = self.client.post( reverse('nodes_handler'), { 'op': 'new', 'architecture': architecture, 'mac_addresses': [factory.getRandomMACAddress()], }) node = Node.objects.get( system_id=json.loads(response.content)['system_id']) self.assertEqual(5, len(strip_domain(node.hostname)))
def test_POST_create_with_no_hostname_auto_populates_hostname(self): architecture = make_usable_architecture(self) response = self.client.post( reverse("machines_handler"), { "architecture": architecture, "power_type": "manual", "mac_addresses": [factory.make_mac_address()], }, ) machine = Machine.objects.get( system_id=json_load_bytes(response.content)["system_id"]) self.assertNotEqual("", strip_domain(machine.hostname))
def fqdn(self): """Fully qualified domain name for this node. If MAAS manages DNS for this node, the domain part of the hostname (if present), is replaced by the domain configured on the cluster controller. If not, simply return the node's hostname. """ # Avoid circular imports. from maasserver.dns import is_dns_managed if is_dns_managed(self.nodegroup): # If the hostname field contains a domain, strip it. hostname = strip_domain(self.hostname) # Build the FQDN by using the hostname and nodegroup.name # as the domain name. return '%s.%s' % (hostname, self.nodegroup.name) else: return self.hostname