1
# Copyright 2012 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Reconcile MAAS's view of the world with the Provisioning Server's.
6
Both MAAS and the Provisioning Server will be examined. Missing nodes will be
7
copied in *both* directions. Nodes that exist in common will have their MAC
8
addresses reconciled in *both* directions. The result should be the union of
9
nodes in MAAS and nodes as the Provisioning Server sees them.
11
The Provisioning Server is currently stateless, so this actually implies
12
reconciling with Cobbler.
14
************************************************************************
15
** DO NOT USE ON A PRODUCTION SYSTEM **
16
** This is intended for use as a QA tool only **
17
************************************************************************
20
from __future__ import (
31
from django.core.management.base import NoArgsCommand
32
from maasserver import provisioning
33
from maasserver.enum import ARCHITECTURE
34
from maasserver.models import Node
37
ARCHITECTURE_GUESSES = {
38
"i386": ARCHITECTURE.i386,
39
"amd64": ARCHITECTURE.amd64,
40
"x86_64": ARCHITECTURE.amd64,
44
def guess_architecture_from_profile(profile_name):
46
This attempts to obtain the architecture from a Cobbler profile name. The
47
naming convention for profile names is "maas-${series}-${arch}".
49
for guess, architecture in ARCHITECTURE_GUESSES.items():
50
if guess in profile_name:
57
papi = provisioning.get_provisioning_api_proxy()
58
nodes_local = {node.system_id: node for node in Node.objects.all()}
59
nodes_remote = papi.get_nodes()
61
missing_local = set(nodes_remote).difference(nodes_local)
62
missing_local.discard("default")
63
for name in missing_local:
64
print("remote:", name)
65
remote_node = nodes_remote[name]
67
system_id=remote_node["name"],
69
guess_architecture_from_profile(remote_node["profile"])),
70
power_type=remote_node["power_type"],
71
hostname=remote_node["name"])
73
for mac_address in remote_node["mac_addresses"]:
74
local_node.add_mac_address(mac_address)
76
missing_remote = set(nodes_local).difference(nodes_remote)
77
for name in missing_remote:
79
local_node = nodes_local[name]
80
provisioning.provision_post_save_Node(
81
sender=None, instance=local_node, created=False)
83
present_in_both = set(nodes_local).intersection(nodes_remote)
84
for name in present_in_both:
85
print("common:", name)
86
node_local = nodes_local[name]
87
node_remote = nodes_remote[name]
88
# Check that MACs are the same.
91
for mac in node_local.macaddress_set.all()
93
print("- local macs:", " ".join(sorted(macs_local)))
95
mac for mac in node_remote["mac_addresses"]
97
print("- remote macs:", " ".join(sorted(macs_remote)))
98
for mac in macs_remote - macs_local:
99
node_local.add_mac_address(mac)
100
if len(macs_local - macs_remote) != 0:
101
provisioning.set_node_mac_addresses(node_local)
104
class Command(NoArgsCommand):
108
def handle_noargs(self, **options):