~rvb/maas/transaction-1.7-bug-1409852

« back to all changes in this revision

Viewing changes to src/provisioningserver/rpc/dhcp.py

merged upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
 
14
14
__metaclass__ = type
15
15
__all__ = [
16
 
    "configure_dhcpv6",
17
16
    "create_host_maps",
 
17
    "DHCPv4Server",
 
18
    "DHCPv6Server",
18
19
    "remove_host_maps",
19
20
]
20
21
 
 
22
from abc import (
 
23
    ABCMeta,
 
24
    abstractmethod,
 
25
    abstractproperty,
 
26
    )
 
27
 
 
28
from provisioningserver.dhcp import control
21
29
from provisioningserver.dhcp.config import get_config
22
 
from provisioningserver.dhcp.control import (
23
 
    restart_dhcpv6,
24
 
    stop_dhcpv6,
25
 
    )
 
30
from provisioningserver.dhcp.omshell import Omshell
26
31
from provisioningserver.logger import get_maas_logger
27
 
from provisioningserver.omshell import Omshell
28
32
from provisioningserver.rpc.exceptions import (
29
33
    CannotConfigureDHCP,
30
34
    CannotCreateHostMap,
36
40
 
37
41
maaslog = get_maas_logger("dhcp")
38
42
 
 
43
# Location of the DHCPv4 configuration file.
 
44
DHCPv4_CONFIG_FILE = '/etc/maas/dhcpd.conf'
 
45
 
 
46
# Location of the DHCPv4 interfaces file.
 
47
DHCPv4_INTERFACES_FILE = '/var/lib/maas/dhcpd-interfaces'
 
48
 
39
49
# Location of the DHCPv6 configuration file.
40
50
DHCPv6_CONFIG_FILE = '/etc/maas/dhcpd6.conf'
41
51
 
42
52
# Location of the DHCPv6 interfaces file.
43
53
DHCPv6_INTERFACES_FILE = '/var/lib/maas/dhcpd6-interfaces'
44
54
 
45
 
 
46
 
def configure_dhcpv6(omapi_key, subnet_configs):
47
 
    """Configure the DHCPv6 server, and restart it as appropriate.
48
 
 
49
 
    :param omapi_key: OMAPI secret key.
50
 
    :param subnet_configs: List of dicts with subnet parameters for each
51
 
        subnet for which the DHCP server should serve DHCPv6.  If no subnets
52
 
        are defined, the DHCP server will be stopped.
 
55
# Message to put in the DHCP config file when the DHCP server gets stopped.
 
56
DISABLED_DHCP_SERVER = "# DHCP server stopped and disabled."
 
57
 
 
58
 
 
59
class DHCPServer:
 
60
    """Represents the settings and controls for a DHCP server.
 
61
 
 
62
    :cvar descriptive_name: A name to use for this server in human-readable
 
63
        texts.
 
64
    :cvar template_basename: The base filename for the template to use when
 
65
        generating configuration for this server.
 
66
    :cvar interfaces_filename: The full path and filename for the server's
 
67
        interfaces file.
 
68
    :cvar config_filename: The full path and filename for the server's
 
69
        configuration file.
 
70
    :ivar omapi_key: The OMAPI secret key for the server.
53
71
    """
54
72
 
55
 
    interfaces = ' '.join(
56
 
        sorted({subnet['interface'] for subnet in subnet_configs}))
57
 
    dhcpd_config = get_config(
58
 
        'dhcpd6.conf.template',
59
 
        omapi_key=omapi_key, dhcp_subnets=subnet_configs)
60
 
    try:
61
 
        sudo_write_file(DHCPv6_CONFIG_FILE, dhcpd_config)
62
 
        sudo_write_file(DHCPv6_INTERFACES_FILE, interfaces)
63
 
    except ExternalProcessError as e:
64
 
        # ExternalProcessError.__unicode__ contains a generic failure message
65
 
        # as well as the command and its error output.  On the other hand,
66
 
        # ExternalProcessError.output_as_unicode contains just the error
67
 
        # output which is probably the best information on what went wrong.
68
 
        # Log the full error information, but keep the exception message short
69
 
        # and to the point.
70
 
        maaslog.error(
71
 
            "Could not rewrite DHCPv6 server configuration "
72
 
            "(for network interfaces %s): %s",
73
 
            ', '.join(interfaces), unicode(e))
74
 
        raise CannotConfigureDHCP(
75
 
            "Could not rewrite DHCPv6 server configuration: %s"
76
 
            % e.output_as_unicode)
77
 
 
78
 
    if len(subnet_configs) == 0:
79
 
        try:
80
 
            stop_dhcpv6()
81
 
        except ExternalProcessError as e:
82
 
            maaslog.error("DHCPv6 server failed to stop: %s", unicode(e))
83
 
            raise CannotConfigureDHCP(
84
 
                "DHCPv6 server failed to stop: %s" % e.output_as_unicode)
85
 
    else:
86
 
        try:
87
 
            restart_dhcpv6()
88
 
        except ExternalProcessError as e:
 
73
    __metaclass__ = ABCMeta
 
74
 
 
75
    descriptive_name = abstractproperty()
 
76
    template_basename = abstractproperty()
 
77
    interfaces_filename = abstractproperty()
 
78
    config_filename = abstractproperty()
 
79
 
 
80
    def __init__(self, omapi_key):
 
81
        super(DHCPServer, self).__init__()
 
82
        self.omapi_key = omapi_key
 
83
 
 
84
    @abstractmethod
 
85
    def stop(self):
 
86
        """Stop the DHCP server."""
 
87
 
 
88
    @abstractmethod
 
89
    def restart(self):
 
90
        """Restart the DHCP server."""
 
91
 
 
92
    def configure(self, subnet_configs):
 
93
        """Configure the DHCPv6/DHCPv4 server, and restart it as appropriate.
 
94
 
 
95
        :param subnet_configs: List of dicts with subnet parameters for each
 
96
            subnet for which the DHCP server should serve DHCP. If no subnets
 
97
            are defined, the DHCP server will be stopped.
 
98
        """
 
99
        stopping = len(subnet_configs) == 0
 
100
 
 
101
        if stopping:
 
102
            dhcpd_config = DISABLED_DHCP_SERVER
 
103
        else:
 
104
            dhcpd_config = get_config(
 
105
                self.template_basename, omapi_key=self.omapi_key,
 
106
                dhcp_subnets=subnet_configs)
 
107
 
 
108
        interfaces = {subnet['interface'] for subnet in subnet_configs}
 
109
        interfaces_config = ' '.join(sorted(interfaces))
 
110
 
 
111
        try:
 
112
            sudo_write_file(self.config_filename, dhcpd_config)
 
113
            sudo_write_file(self.interfaces_filename, interfaces_config)
 
114
        except ExternalProcessError as e:
 
115
            # ExternalProcessError.__unicode__ contains a generic failure
 
116
            # message as well as the command and its error output. On the
 
117
            # other hand, ExternalProcessError.output_as_unicode contains just
 
118
            # the error output which is probably the best information on what
 
119
            # went wrong. Log the full error information, but keep the
 
120
            # exception message short and to the point.
89
121
            maaslog.error(
90
 
                "DHCPv6 server failed to restart "
91
 
                "(for network interfaces %s): %s",
92
 
                ', '.join(interfaces), unicode(e))
 
122
                "Could not rewrite %s server configuration (for network "
 
123
                "interfaces %s): %s", self.descriptive_name,
 
124
                interfaces_config, unicode(e))
93
125
            raise CannotConfigureDHCP(
94
 
                "DHCPv6 server failed to restart: %s" % e.output_as_unicode)
 
126
                "Could not rewrite %s server configuration: %s" % (
 
127
                    self.descriptive_name, e.output_as_unicode))
 
128
 
 
129
        if stopping:
 
130
            try:
 
131
                self.stop()
 
132
            except ExternalProcessError as e:
 
133
                maaslog.error(
 
134
                    "%s server failed to stop: %s", self.descriptive_name,
 
135
                    unicode(e))
 
136
                raise CannotConfigureDHCP(
 
137
                    "%s server failed to stop: %s" % (
 
138
                        self.descriptive_name, e.output_as_unicode))
 
139
        else:
 
140
            try:
 
141
                self.restart()
 
142
            except ExternalProcessError as e:
 
143
                maaslog.error(
 
144
                    "%s server failed to restart (for network interfaces "
 
145
                    "%s): %s", self.descriptive_name, interfaces_config,
 
146
                    unicode(e))
 
147
                raise CannotConfigureDHCP(
 
148
                    "%s server failed to restart: %s" % (
 
149
                        self.descriptive_name, e.output_as_unicode))
 
150
 
 
151
 
 
152
class DHCPv4Server(DHCPServer):
 
153
    """Represents the settings and controls for a DHCPv4 server.
 
154
 
 
155
    See `DHCPServer`.
 
156
    """
 
157
 
 
158
    descriptive_name = "DHCPv4"
 
159
    template_basename = 'dhcpd.conf.template'
 
160
    interfaces_filename = DHCPv4_INTERFACES_FILE
 
161
    config_filename = DHCPv4_CONFIG_FILE
 
162
 
 
163
    def stop(self):
 
164
        """Stop the DHCPv4 server."""
 
165
        control.stop_dhcpv4()
 
166
 
 
167
    def restart(self):
 
168
        """Restart the DHCPv4 server."""
 
169
        control.restart_dhcpv4()
 
170
 
 
171
 
 
172
class DHCPv6Server(DHCPServer):
 
173
    """Represents the settings and controls for a DHCPv6 server.
 
174
 
 
175
    See `DHCPServer`.
 
176
    """
 
177
 
 
178
    descriptive_name = "DHCPv6"
 
179
    template_basename = 'dhcpd6.conf.template'
 
180
    interfaces_filename = DHCPv6_INTERFACES_FILE
 
181
    config_filename = DHCPv6_CONFIG_FILE
 
182
 
 
183
    def stop(self):
 
184
        """Stop the DHCPv6 server."""
 
185
        control.stop_dhcpv6()
 
186
 
 
187
    def restart(self):
 
188
        """Restart the DHCPv6 server."""
 
189
        control.restart_dhcpv6()
95
190
 
96
191
 
97
192
def create_host_maps(mappings, shared_key):