Esempio n. 1
0
    def __init__(self, writeDir, execCommands=True):
        """
        writeDir: directory to use for generated config files (e.g. hostapd.conf).
        execCommands: whether or not to run commands (set to False for testing).
        """
        self.writeDir = writeDir
        self.execCommands = execCommands

        # Make sure directory exists.
        pdosq.makedirs(writeDir)

        self.previousCommands = list()
        self.currentConfig = dict()
        self.nextSectionId = 0

        # Number of objects requiring IP forwarding.
        # If >0, we need to enable system-wide.
        # If ==0, we can probably disable.
        self.forwardingCount = 0

        # Track whether we have loaded the conntrack kernel module.  We need to
        # make sure the module is loaded before adding iptables rules related
        # to connection state.
        self.conntrackLoaded = False

        # Allow threads to wait for first load to complete.  This will be set
        # after the first load completes and will remain set thereafter.
        self.systemUp = threading.Event()

        # Track epoch number so that we know which configuration sections
        # were applied in the most recent epoch.
        self.epoch = 0
Esempio n. 2
0
    def __init__(self, writeDir, execCommands=True):
        """
        writeDir: directory to use for generated config files (e.g. hostapd.conf).
        execCommands: whether or not to run commands (set to False for testing).
        """
        self.writeDir = writeDir
        self.execCommands = execCommands

        # Make sure directory exists.
        pdosq.makedirs(writeDir)

        self.previousCommands = list()
        self.currentConfig = dict()
        self.nextSectionId = 0

        # Number of objects requiring IP forwarding.
        # If >0, we need to enable system-wide.
        # If ==0, we can probably disable.
        self.forwardingCount = 0

        # Track whether we have loaded the conntrack kernel module.  We need to
        # make sure the module is loaded before adding iptables rules related
        # to connection state.
        self.conntrackLoaded = False

        # Allow threads to wait for first load to complete.  This will be set
        # after the first load completes and will remain set thereafter.
        self.systemUp = threading.Event()

        # Track epoch number so that we know which configuration sections
        # were applied in the most recent epoch.
        self.epoch = 0
Esempio n. 3
0
def loadSettings(mode="local", slist=[]):
    """
    Take a list of key:value pairs, and replace any setting defined.
    Also search through the settings module and see if any matching
    environment variables exist to replace as well.

    :param slist: the list of key:val settings
    :type slist: array.

    :returns: None
    """

    # Get a handle to our settings defined above
    mod = sys.modules[__name__]

    # Adjust default paths if we are running under ubuntu snappy
    snapPath = os.environ.get("SNAP", None)
    snapCommonPath = os.environ.get("SNAP_COMMON", None)
    snapDataPath = os.environ.get("SNAP_DATA", None)

    # Directories where we might find a settings.ini file.
    settings_file_dirs = [".", "/etc"]

    if mode == "local":
        updatePaths(os.path.join(os.path.expanduser("~"), ".paradrop/"),
                                 "/tmp/.paradrop/")
        mod.HOST_DATA_PARTITION = mod.CONFIG_HOME_DIR
    elif mode == "unittest":
        updatePaths("/tmp/.paradrop-test/", "/tmp/.paradrop-test/")
        mod.HOST_DATA_PARTITION = mod.CONFIG_HOME_DIR
    elif snapCommonPath is not None:
        settings_file_dirs.append(snapCommonPath)
        updatePaths(snapCommonPath, snapDataPath)
        mod.HOST_DATA_PARTITION = "/writable"
        mod.DOCKER_BIN_DIR = "/snap/bin"
        mod.GOVERNOR_INTERFACE = os.path.join(snapPath, "governor", "governor.socket")
        mod.ZEROTIER_LIB_DIR = os.path.join(snapPath, "zerotier-one")

    for x in [mod.LOG_DIR, mod.KEY_DIR, mod.MISC_DIR]:
        pdosq.makedirs(x)

    # First overwrite settings they may have provided with the arg list
    for kv in slist:
        k, v = kv.split(':', 1)
        # We can either replace an existing setting, or set a new value, we don't care
        setattr(mod, k, parseValue(v))

    # Next check for settings from file(s) which may be located in a few
    # different directories.
    for d in settings_file_dirs:
        path = os.path.join(d, constants.SETTINGS_FILE_NAME)
        load_from_file(path)

    # Now search through our settings and look for environment variable matches
    # they defined. Environment variables override all other sources.
    for name, _ in iterate_module_attributes(mod):
        # Check for an environment variable matching the setting.
        value = os.environ.get(name, None)
        if value is not None:
            setattr(mod, name, parseValue(value))
Esempio n. 4
0
def loadSettings(mode="local", slist=[]):
    """
    Take a list of key:value pairs, and replace any setting defined.
    Also search through the settings module and see if any matching
    environment variables exist to replace as well.

    :param slist: the list of key:val settings
    :type slist: array.

    :returns: None
    """

    # Get a handle to our settings defined above
    mod = sys.modules[__name__]

    # Adjust default paths if we are running under ubuntu snappy
    snapPath = os.environ.get("SNAP", None)
    snapCommonPath = os.environ.get("SNAP_COMMON", None)
    snapDataPath = os.environ.get("SNAP_DATA", None)

    # Directories where we might find a settings.ini file.
    settings_file_dirs = [".", "/etc"]

    if mode == "local":
        updatePaths(os.path.join(os.path.expanduser("~"), ".paradrop/"),
                                 "/tmp/.paradrop/")
        mod.HOST_DATA_PARTITION = mod.CONFIG_HOME_DIR
    elif mode == "unittest":
        updatePaths("/tmp/.paradrop-test/", "/tmp/.paradrop-test/")
        mod.HOST_DATA_PARTITION = mod.CONFIG_HOME_DIR
    elif snapCommonPath is not None:
        settings_file_dirs.append(snapCommonPath)
        updatePaths(snapCommonPath, snapDataPath)
        mod.HOST_DATA_PARTITION = "/writable"
        mod.DOCKER_BIN_DIR = "/snap/bin"
        mod.GOVERNOR_INTERFACE = os.path.join(snapPath, "governor", "governor.socket")
        mod.ZEROTIER_LIB_DIR = os.path.join(snapPath, "zerotier-one")

    for x in [mod.LOG_DIR, mod.KEY_DIR, mod.MISC_DIR]:
        pdosq.makedirs(x)

    # First overwrite settings they may have provided with the arg list
    for kv in slist:
        k, v = kv.split(':', 1)
        # We can either replace an existing setting, or set a new value, we don't care
        setattr(mod, k, parseValue(v))

    # Next check for settings from file(s) which may be located in a few
    # different directories.
    for d in settings_file_dirs:
        path = os.path.join(d, constants.SETTINGS_FILE_NAME)
        load_from_file(path)

    # Now search through our settings and look for environment variable matches
    # they defined. Environment variables override all other sources.
    for name, _ in iterate_module_attributes(mod):
        # Check for an environment variable matching the setting.
        value = os.environ.get(name, None)
        if value is not None:
            setattr(mod, name, parseValue(value))
Esempio n. 5
0
 def backup(self, backupToken):
     """
         Puts a backup of this config to the location specified in @backupPath.
     """
     pdosq.makedirs(settings.UCI_BACKUP_DIR)
     backupPath = "{}/{}-{}".format(settings.UCI_BACKUP_DIR, self.myname,
             backupToken)
     pdos.copy(self.filepath, backupPath)
Esempio n. 6
0
def createVolumeDirs(update):
    extDataDir = update.new.getCache('externalDataDir')
    extSystemDir = update.new.getCache('externalSystemDir')

    if update.updateType == 'delete':
        pdos.remove(extDataDir, suppressNotFound=True)
        pdos.remove(extSystemDir, suppressNotFound=True)
    else:
        pdosq.makedirs(extDataDir)
        pdosq.makedirs(extSystemDir)
        os.chown(extDataDir, settings.CONTAINER_UID, -1)
Esempio n. 7
0
def createVolumeDirs(update):
    """
    Create directories required by the chute.
    """
    extDataDir = update.cache_get('externalDataDir')
    extSystemDir = update.cache_get('externalSystemDir')

    if update.updateType == 'delete':
        pdos.remove(extDataDir, suppressNotFound=True)
        pdos.remove(extSystemDir, suppressNotFound=True)
    else:
        pdosq.makedirs(extDataDir)
        pdosq.makedirs(extSystemDir)
        os.chown(extDataDir, settings.CONTAINER_UID, -1)
Esempio n. 8
0
    def __init__(self, writeDir):
        self.writeDir = writeDir

        # Make sure directory exists.
        pdosq.makedirs(writeDir)

        self.previousCommands = list()
        self.currentConfig = dict()
        self.nextSectionId = 0

        # Number of objects requiring IP forwarding.
        # If >0, we need to enable system-wide.
        # If ==0, we can probably disable.
        self.forwardingCount = 0

        # Allow threads to wait for first load to complete.  This will be set
        # after the first load completes and will remain set thereafter.
        self.systemUp = threading.Event()
Esempio n. 9
0
def createVolumeDirs(update):
    """
    Create directories required by the chute.
    """
    extDataDir = update.cache_get('externalDataDir')
    extSystemDir = update.cache_get('externalSystemDir')

    if update.updateType == 'delete':
        pdos.remove(extDataDir, suppressNotFound=True)
        pdos.remove(extSystemDir, suppressNotFound=True)
    else:
        pdosq.makedirs(extDataDir)
        pdosq.makedirs(extSystemDir)
        try:
            os.chown(extDataDir, settings.CONTAINER_UID, -1)
        except:
            # chown is not permitted in strict confinement.
            # TODO: do we need to chmod the data directory, or does it work
            # fine regardless?
            pass
Esempio n. 10
0
def test_pdosq():
    """
    Test pdosq utility functions
    """
    # Test makedirs with an already-exists error, returns False.
    with patch('os.makedirs', side_effect=OSError(errno.EEXIST, "error")):
        assert pdosq.makedirs("/") is False

    # Test makedirs with a permission error, passes on the Exception.
    with patch('os.makedirs', side_effect=OSError(errno.EPERM, "error")):
        assert_raises(OSError, pdosq.makedirs, "/")
Esempio n. 11
0
def createVolumeDirs(update):
    """
    Create directories required by the chute.
    """
    extDataDir = update.cache_get('externalDataDir')
    extSystemDir = update.cache_get('externalSystemDir')

    if update.updateType == 'delete':
        pdos.remove(extDataDir, suppressNotFound=True)
        pdos.remove(extSystemDir, suppressNotFound=True)
    else:
        pdosq.makedirs(extDataDir)
        pdosq.makedirs(extSystemDir)
        try:
            os.chown(extDataDir, settings.CONTAINER_UID, -1)
        except:
            # chown is not permitted in strict confinement.
            # TODO: do we need to chmod the data directory, or does it work
            # fine regardless?
            pass
Esempio n. 12
0
def test_pdosq():
    """
    Test pdosq utility functions
    """
    # Test makedirs with an already-exists error, returns False.
    with patch('os.makedirs', side_effect=OSError(errno.EEXIST, "error")):
        assert pdosq.makedirs("/") is False

    # Test makedirs with a permission error, passes on the Exception.
    with patch('os.makedirs', side_effect=OSError(errno.EPERM, "error")):
        assert_raises(OSError, pdosq.makedirs, "/")
Esempio n. 13
0
    def apply(self, allConfigs):
        commands = list()

        # visibleName will be used in choosing file names for this dnsmasq
        # instance, must be unique if there are multiple dnsmasq instances
        visibleName = self.internalName

        if self.interface is None:
            interfaces = []
            for section in self.findByType(allConfigs, "dhcp", "dhcp"):
                interfaces.append(section.interface)
        else:
            interfaces = self.interface

        self.__leasefile = self.leasefile
        if self.__leasefile is None:
            self.__leasefile = "{}/dnsmasq-{}.leases".format(
                self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(self.__leasefile))

        pidFile = "{}/dnsmasq-{}.pid".format(
            self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(pidFile))

        outputPath = "{}/dnsmasq-{}.conf".format(
            self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(outputPath))

        with open(outputPath, "w") as outputFile:
            outputFile.write("#" * 80 + "\n")
            outputFile.write("# dnsmasq configuration file generated by "
                             "pdconfd\n")
            outputFile.write("# Source: {}\n".format(self.source))
            outputFile.write("# Section: {}\n".format(str(self)))
            outputFile.write("#" * 80 + "\n")
            outputFile.write("\n")
            outputFile.write("dhcp-leasefile={}\n".format(self.__leasefile))

            if self.authoritative:
                outputFile.write("dhcp-authoritative\n")
            outputFile.write("cache-size={}\n".format(self.cachesize))
            if self.dhcp_boot is not None:
                outputFile.write("dhcp-boot={}\n".format(self.dhcp_boot))
            outputFile.write("dhcp-lease-max={}\n".format(self.dhcpleasemax))
            if self.domain:
                outputFile.write("domain={}\n".format(self.domain))
            if self.enable_tftp:
                outputFile.write("enable-tftp\n")
            if self.expandhosts:
                outputFile.write("expand-hosts\n")
            if self.noresolv:
                outputFile.write("no-resolv\n")
            if self.tftp_root is not None:
                outputFile.write("tftp-root={}\n".format(self.tftp_root))

            if self.server:
                for server in self.server:
                    outputFile.write("server={}\n".format(server))

            # TODO: Bind interfaces allows us to have multiple instances of
            # dnsmasq running, but it would probably be better to have one
            # running and reconfigure it when we want to add or remove
            # interfaces.  It is not very disruptive to reconfigure and restart
            # dnsmasq.
            outputFile.write("\n")
            outputFile.write("except-interface=lo\n")
            outputFile.write("bind-interfaces\n")

            for intfName in interfaces:
                interface = self.lookup(allConfigs, "network", "interface", intfName)
                outputFile.write("\n")
                outputFile.write("# Options for section interface {}\n".
                                 format(interface.name))
                outputFile.write("interface={}\n".format(
                                 interface.config_ifname))

                network = ipaddress.IPv4Network(u"{}/{}".format(
                    interface.ipaddr, interface.netmask), strict=False)

                dhcp = self.lookup(allConfigs, "dhcp", "dhcp", intfName)

                outputFile.write("\n")
                outputFile.write("# Options for section dhcp {}\n".
                                 format(interface.name))

                if None not in [dhcp.start, dhcp.limit, dhcp.leasetime]:
                    # TODO: Error checking!
                    firstAddress = network.network_address + dhcp.start
                    lastAddress = firstAddress + dhcp.limit

                    outputFile.write("dhcp-range={},{},{},{}\n".format(
                        str(firstAddress), str(lastAddress), interface.netmask,
                        dhcp.leasetime))

                # Write options sections to the config file.
                for option in dhcp.dhcp_option:
                    outputFile.write("dhcp-option={}\n".format(option))

                for relay in dhcp.relay:
                    outputFile.write("dhcp-relay={}\n".format(relay))
                if dhcp.relay:
                    outputFile.write("dhcp-proxy\n")

            outputFile.write("\n")

            for domain in self.findByType(allConfigs, "dhcp", "domain"):
                outputFile.write("address=/{}/{}\n".format(domain.name, domain.ip))

        cmd = ["dnsmasq", "--conf-file={}".format(outputPath),
               "--pid-file={}".format(pidFile)]
        commands.append((self.PRIO_START_DAEMON, Command(cmd, self)))

        self.pidFile = pidFile
        return commands
Esempio n. 14
0
def test_pdoqs(mOs):
    assert pdosq.makedirs('test')
    mOs.makedirs.assert_called_once_with('test')
Esempio n. 15
0
def getSystemConfigDir():
    base = settings.UCI_CONFIG_DIR
    pdosq.makedirs(base)
    return base
Esempio n. 16
0
def test_pdoqs(mOs):
    assert pdosq.makedirs('test')
    mOs.makedirs.assert_called_once_with('test')
Esempio n. 17
0
def getSystemConfigDir():
    base = settings.UCI_CONFIG_DIR
    pdosq.makedirs(base)
    return base
Esempio n. 18
0
    def apply(self, allConfigs):
        commands = list()

        # visibleName will be used in choosing file names for this dnsmasq
        # instance, must be unique if there are multiple dnsmasq instances
        visibleName = self.internalName

        if self.interface is None:
            interfaces = []
            for section in self.findByType(allConfigs, "dhcp", "dhcp"):
                interfaces.append(section.interface)
        else:
            interfaces = self.interface

        self.__leasefile = self.leasefile
        if self.__leasefile is None:
            self.__leasefile = "{}/dnsmasq-{}.leases".format(
                self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(self.__leasefile))

        pidFile = "{}/dnsmasq-{}.pid".format(
            self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(pidFile))

        outputPath = "{}/dnsmasq-{}.conf".format(
            self.manager.writeDir, visibleName)
        pdosq.makedirs(os.path.dirname(outputPath))

        with open(outputPath, "w") as outputFile:
            outputFile.write("#" * 80 + "\n")
            outputFile.write("# dnsmasq configuration file generated by "
                             "pdconfd\n")
            outputFile.write("# Source: {}\n".format(self.source))
            outputFile.write("# Section: {}\n".format(str(self)))
            outputFile.write("#" * 80 + "\n")
            outputFile.write("\n")
            outputFile.write("dhcp-leasefile={}\n".format(self.__leasefile))

            if self.authoritative:
                outputFile.write("dhcp-authoritative\n")
            outputFile.write("cache-size={}\n".format(self.cachesize))
            if self.dhcp_boot is not None:
                outputFile.write("dhcp-boot={}\n".format(self.dhcp_boot))
            outputFile.write("dhcp-lease-max={}\n".format(self.dhcpleasemax))
            if self.domain:
                outputFile.write("domain={}\n".format(self.domain))
            if self.enable_tftp:
                outputFile.write("enable-tftp\n")
            if self.expandhosts:
                outputFile.write("expand-hosts\n")
            if self.noresolv:
                outputFile.write("no-resolv\n")
            if self.tftp_root is not None:
                outputFile.write("tftp-root={}\n".format(self.tftp_root))

            if self.server:
                for server in self.server:
                    outputFile.write("server={}\n".format(server))

            # TODO: Bind interfaces allows us to have multiple instances of
            # dnsmasq running, but it would probably be better to have one
            # running and reconfigure it when we want to add or remove
            # interfaces.  It is not very disruptive to reconfigure and restart
            # dnsmasq.
            outputFile.write("\n")
            outputFile.write("except-interface=lo\n")
            outputFile.write("bind-interfaces\n")

            for intfName in interfaces:
                interface = self.lookup(allConfigs, "network", "interface", intfName)
                outputFile.write("\n")
                outputFile.write("# Options for section interface {}\n".
                                 format(interface.name))
                outputFile.write("interface={}\n".format(
                                 interface.config_ifname))

                network = ipaddress.IPv4Network(u"{}/{}".format(
                    interface.ipaddr, interface.netmask), strict=False)

                dhcp = self.lookup(allConfigs, "dhcp", "dhcp", intfName)

                outputFile.write("\n")
                outputFile.write("# Options for section dhcp {}\n".
                                 format(interface.name))

                if None not in [dhcp.start, dhcp.limit, dhcp.leasetime]:
                    # TODO: Error checking!
                    firstAddress = network.network_address + dhcp.start
                    lastAddress = firstAddress + dhcp.limit

                    outputFile.write("dhcp-range={},{},{},{}\n".format(
                        str(firstAddress), str(lastAddress), interface.netmask,
                        dhcp.leasetime))

                # Write options sections to the config file.
                for option in dhcp.dhcp_option:
                    outputFile.write("dhcp-option={}\n".format(option))

                for relay in dhcp.relay:
                    outputFile.write("dhcp-relay={}\n".format(relay))
                if dhcp.relay:
                    outputFile.write("dhcp-proxy\n")

            outputFile.write("\n")

            for domain in self.findByType(allConfigs, "dhcp", "domain"):
                outputFile.write("address=/{}/{}\n".format(domain.name, domain.ip))

        cmd = ["dnsmasq", "--conf-file={}".format(outputPath),
               "--pid-file={}".format(pidFile)]
        commands.append((self.PRIO_START_DAEMON, Command(cmd, self)))

        self.pidFile = pidFile
        return commands