class DHCPv6Server(DHCPServer): """Represents the settings for a DHCPv6 server. See `DHCPServer`. """ descriptive_name = "DHCPv6" template_basename = "dhcpd6.conf.template" interfaces_filename = get_maas_data_path(DHCPv6_INTERFACES_FILE) config_filename = get_maas_data_path(DHCPv6_CONFIG_FILE) dhcp_service = "dhcpd6" ipv6 = True
def test_can_write_file_in_development(self): filename = get_maas_data_path("dhcpd.conf") contents = factory.make_bytes() # Binary safe. mode = random.randint(0o000, 0o777) | 0o400 # Always u+r. sudo_write_file(filename, contents, mode) self.assertThat(filename, FileContains(contents)) self.assertThat(os.stat(filename).st_mode & 0o777, Equals(mode))
def _makeTFTPService(self, tftp_root, tftp_port, rpc_service): """Create the dynamic TFTP service.""" from provisioningserver.rackdservices.tftp import TFTPService tftp_service = TFTPService(resource_root=tftp_root, port=tftp_port, client_service=rpc_service) tftp_service.setName("tftp") # *** EXPERIMENTAL *** # https://code.launchpad.net/~allenap/maas/tftp-offload/+merge/312146 # If the TFTP port has been set to zero, use the experimental offload # service. Otherwise stick to the normal in-process TFTP service. if tftp_port == 0: from provisioningserver.path import get_maas_data_path from provisioningserver.rackdservices import tftp_offload from twisted.internet.endpoints import UNIXServerEndpoint tftp_offload_socket = get_maas_data_path("tftp-offload.sock") tftp_offload_endpoint = UNIXServerEndpoint(reactor, tftp_offload_socket, wantPID=False) tftp_offload_service = tftp_offload.TFTPOffloadService( reactor, tftp_offload_endpoint, tftp_service.backend) tftp_offload_service.setName("tftp-offload") return tftp_offload_service # *** /EXPERIMENTAL *** return tftp_service
def get_config_v4( template_name: str, global_dhcp_snippets: Sequence[dict], failover_peers: Sequence[dict], shared_networks: Sequence[dict], hosts: Sequence[dict], omapi_key: str, ) -> str: """Return a DHCP config file based on the supplied parameters. :param template_name: Template file name: `dhcpd.conf.template` for the IPv4 template. :return: A full configuration, as a string. """ platform_codename = lsb_release()["codename"] template = load_template("dhcp", template_name) dhcp_socket = get_maas_data_path("dhcpd.sock") # Helper functions to stuff into the template namespace. helpers = { "oneline": normalise_whitespace, "commalist": normalise_any_iterable_to_comma_list, "quoted_commalist": normalise_any_iterable_to_quoted_comma_list, "running_in_snap": snappy.running_in_snap(), } for shared_network in shared_networks: interface = shared_network.get("interface", None) for subnet in shared_network["subnets"]: rack_ip = get_rack_ip_for_subnet( 4, subnet["subnet_cidr"], interface ) if rack_ip is not None: subnet["next_server"] = rack_ip subnet["bootloader"] = compose_conditional_bootloader( False, rack_ip ) ntp_servers = subnet["ntp_servers"] # Is a list. ntp_servers_ipv4, ntp_servers_ipv6 = _get_addresses(*ntp_servers) subnet["ntp_servers_ipv4"] = ", ".join(ntp_servers_ipv4) subnet["ntp_servers_ipv6"] = ", ".join(ntp_servers_ipv6) try: return template.substitute( global_dhcp_snippets=global_dhcp_snippets, hosts=hosts, failover_peers=failover_peers, shared_networks=shared_networks, platform_codename=platform_codename, omapi_key=omapi_key, dhcp_helper=(get_path("/usr/sbin/maas-dhcp-helper")), dhcp_socket=dhcp_socket, **helpers ) except (KeyError, NameError) as error: raise DHCPConfigError( "Failed to render DHCP configuration." ) from error
class ClusterConfiguration(Configuration, metaclass=ClusterConfigurationMeta): """Local configuration for the MAAS cluster.""" maas_url = ConfigurationOption( "maas_url", "The HTTP URL(s) for the MAAS region.", ForEach( ExtendedURL(require_tld=False), convert_to_list=True, if_missing=["http://localhost:5240/MAAS"], ), ) # TFTP options. tftp_port = ConfigurationOption( "tftp_port", "The UDP port on which to listen for TFTP requests.", Number(min=0, max=(2 ** 16) - 1, if_missing=69), ) tftp_root = ConfigurationOption( "tftp_root", "The root directory for TFTP resources.", DirectoryString( # Don't validate values that are already stored. accept_python=True, if_missing=get_maas_data_path("boot-resources/current"), ), ) # GRUB options. @property def grub_root(self): "The root directory for GRUB resources." return os.path.join(self.tftp_root, "grub") # NodeGroup UUID Option, used for migrating to rack controller cluster_uuid = ConfigurationOption( "cluster_uuid", "The UUID for this cluster controller", UUIDString(if_missing=UUID_NOT_SET), ) # Debug options. debug = ConfigurationOption( "debug", "Enable debug mode for detailed error and log reporting.", OneWayStringBool(if_missing=False), )
def set_maas_id(system_id): """Set the system_id for this rack/region permanently for MAAS.""" global _maas_id system_id = _normalise_maas_id(system_id) with _maas_id_lock: maas_id_path = get_maas_data_path("maas_id") if system_id is None: try: atomic_delete(maas_id_path) except FileNotFoundError: _maas_id = None # Job done already. else: _maas_id = None else: atomic_write(system_id.encode("ascii"), maas_id_path) _maas_id = system_id
def setUp(self): """Ensures each test starts cleanly, with no pre-existing secret.""" get_secret = self.patch(security, "get_shared_secret_filesystem_path") # Ensure each test uses a different filename for the shared secret, # so that tests cannot interfere with each other. get_secret.return_value = Path( get_maas_data_path("secret-%s" % factory.make_string(16))) # Extremely unlikely, but just in case. self.delete_secret() self.addCleanup( setattr, security, "DEFAULT_ITERATION_COUNT", security.DEFAULT_ITERATION_COUNT, ) # The default high iteration count would make the tests very slow. security.DEFAULT_ITERATION_COUNT = 2 super().setUp()
def get_maas_id(): """Return the system_id for this rack/region controller that is created when either the rack or region first starts. """ global _maas_id with _maas_id_lock: if _maas_id is None: maas_id_path = get_maas_data_path("maas_id") try: with open(maas_id_path, "r", encoding="ascii") as fp: contents = fp.read().strip() except FileNotFoundError: return None else: _maas_id = _normalise_maas_id(contents) return _maas_id else: return _maas_id
def toggle_cprofile(process_name, signum=None, stack=None): """Toggle cProfile profiling of the process. If it's called when no profiling is enabled, profiling will start. If it's called when profiling is enabled, profiling is stopped and the stats are written to $MAAS_DATA/profiling, with the process name and pid in the name. """ global _profile if _profile is None: _profile = cProfile.Profile() _profile.enable() print("Profiling enabled") else: current_time = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") profiling_dir = get_maas_data_path("profiling") os.makedirs(profiling_dir, exist_ok=True) output_filename = f"{process_name}-{os.getpid()}-{current_time}.pyprof" full_filepath = os.path.join(profiling_dir, output_filename) _profile.create_stats() _profile.dump_stats(full_filepath) _profile = None print(f"Profiling disabled. Output written to {full_filepath}")
def get_maas_user_gpghome(): """Return the GPG directory for the `maas` user. Set $GPGHOME to this value ad-hoc when needed. """ return get_maas_data_path("gnupg")
def setUp(self): super().setUp() self.maas_id_path = get_maas_data_path("maas_id") self.addCleanup(env.set_maas_id, None) env.set_maas_id(None)
def get_shared_secret_filesystem_path(): """Return the path to shared-secret on the filesystem.""" return get_maas_data_path("secret")
def test_can_delete_file_in_development(self): filename = get_maas_data_path("dhcpd.conf") with open(filename, "wb") as fd: fd.write(factory.make_bytes()) sudo_delete_file(filename) self.assertThat(filename, Not(FileExists()))
def get_ipc_socket_path(): """Return the path to the IPC socket.""" return os.environ.get("MAAS_IPC_SOCKET_PATH", get_maas_data_path("maas-regiond.sock"))
def get_socket_path(): """Return path to dhcpd.sock.""" return get_maas_data_path("dhcpd.sock")
def test_get_maas_data_path_env(self): path = os.environ.get("MAAS_DATA") self.assertEqual(get_maas_data_path("some/path"), os.path.join(path, "some/path"))
def test_get_maas_data_path_no_env(self): del os.environ["MAAS_DATA"] self.assertEqual(get_maas_data_path("some/path"), "/var/lib/maas/some/path")