Exemplo n.º 1
0
def _setResourceAllocation(allocation):
    client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                                 version='auto')
    for container_name, resources in six.iteritems(allocation):
        out.info("Update chute {} set cpu_shares={}\n".format(
            container_name, resources['cpu_shares']))
        container = client.containers.get(container_name)
        container.update(cpu_shares=resources['cpu_shares'])

        # Using class id 1:1 for prioritized, 1:3 for best effort.
        # Prioritization is implemented in confd/qos.py.  Class-ID is
        # represented in hexadecimal.
        # Reference: https://www.kernel.org/doc/Documentation/cgroup-v1/net_cls.txt
        if resources.get('prioritize_traffic', False):
            classid = "0x10001"
        else:
            classid = "0x10003"

        container = ChuteContainer(container_name)
        try:
            container_id = container.getID()
            fname = "/sys/fs/cgroup/net_cls/docker/{}/net_cls.classid".format(
                container_id)
            with open(fname, "w") as output:
                output.write(classid)
        except Exception as error:
            out.warn("Error setting traffic class: {}\n".format(error))
Exemplo n.º 2
0
    def onJoin(self, details):
        out.info('Router session joined')

        yield self.subscribe(self.updatesPending,
                             self.uriPrefix + 'updatesPending')
        yield self.register(self.update, self.uriPrefix + 'update')
        yield BaseSession.onJoin(self, details)
Exemplo n.º 3
0
def _setResourceAllocation(allocation):
    client = docker.DockerClient(base_url="unix://var/run/docker.sock", version='auto')
    for container_name, resources in six.iteritems(allocation):
        out.info("Update chute {} set cpu_shares={}\n".format(
            container_name, resources['cpu_shares']))
        container = client.containers.get(container_name)
        container.update(cpu_shares=resources['cpu_shares'])

        # Using class id 1:1 for prioritized, 1:3 for best effort.
        # Prioritization is implemented in confd/qos.py.  Class-ID is
        # represented in hexadecimal.
        # Reference: https://www.kernel.org/doc/Documentation/cgroup-v1/net_cls.txt
        if resources.get('prioritize_traffic', False):
            classid = "0x10001"
        else:
            classid = "0x10003"

        container = ChuteContainer(container_name)
        try:
            container_id = container.getID()
            fname = "/sys/fs/cgroup/net_cls/docker/{}/net_cls.classid".format(container_id)
            with open(fname, "w") as output:
                output.write(classid)
        except Exception as error:
            out.warn("Error setting traffic class: {}\n".format(error))
Exemplo n.º 4
0
    def onStop(self):
        if self.session is not None:
            self.session.leave()
        else:
            out.info('No WAMP session found!')

        super(Nexus, self).onStop()
Exemplo n.º 5
0
 def onConnect(self):
     out.info('Router session connected.')
     if (nexus.core.info.pdid and nexus.core.getKey('apitoken')):
         out.info('Starting WAMP-CRA authentication on realm "{}" as user "{}"...'\
                  .format(self.config.realm, nexus.core.info.pdid))
         self.join(self.config.realm, [u'wampcra'],
                   ensure_unicode(nexus.core.info.pdid))
Exemplo n.º 6
0
def removeChute(update):
    """
    Remove a docker container and the image it was built on based on the passed in update.

    :param update: The update object containing information about the chute.
    :type update: obj
    :returns: None
    """
    out.info('Attempting to remove chute %s\n' % (update.name))
    c = docker.DockerClient(base_url='unix://var/run/docker.sock',
                            version='auto')
    repo = getImageName(update.old)
    name = update.name

    cleanup_net_interfaces(update.old)

    try:
        container = c.containers.get(name)
        container.remove(force=True)
    except Exception as e:
        update.progress(str(e))

    try:
        c.images.remove(repo)
    except Exception as e:
        update.progress(str(e))
Exemplo n.º 7
0
    def onStart(self):
        super(Nexus, self).onStart()
        # onStart is called when the reactor starts, not when the connection is made.
        # Check for provisioning keys and attempt to connect
        if not self.provisioned() and not provisioning.can_provision():
            out.warn("The node is not provisioned and is not configured to self-provision.")
            return

        while not self.provisioned():
            yield provisioning.provision_self(self.update_manager)

        try:
            # Set up communication with pdserver.
            # 1. Create a report of the current system state and send that.
            # 2. Send the node public key.
            # 3. Poll for a list of updates that should be applied.
            # 4. Open WAMP session.
            yield sendStateReport()
            yield sendNodeIdentity()
            yield self.update_fetcher.start_polling()
            yield self.connect(WampSession)
        except Exception:
            out.warn('The router ID or password is invalid!')
        else:
            out.info('WAMP session is ready!')
Exemplo n.º 8
0
    def connectionMade(self):
        self.factory.transports.add(self.transport)
        out.info('sockjs /logs connected')

        self.log_provider = LogProvider(self.factory.chute)
        self.log_provider.attach()
        self.loop.start(1.0)
Exemplo n.º 9
0
def call_retry(cmd, env, delay=3, tries=3):
    # Make sure each component of the command is a string.  Otherwisew we will
    # get errors.
    clean_cmd = [str(v) for v in cmd]

    while tries >= 0:
        tries -= 1

        out.info("Calling: {}\n".format(" ".join(clean_cmd)))
        try:
            proc = subprocess.Popen(clean_cmd,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE,
                                    env=env)
            for line in proc.stdout:
                out.info("{}: {}\n".format(clean_cmd[0], line.strip()))
            for line in proc.stderr:
                out.warn("{}: {}\n".format(clean_cmd[0], line.strip()))
            return proc.returncode
        except OSError as e:
            out.warn('Command "{}" failed\n'.format(" ".join(clean_cmd)))
            if tries <= 0:
                out.exception(e, True)
                raise e

        time.sleep(delay)
Exemplo n.º 10
0
    def _update_complete(self, update):
        """
        Internal: callback after an update operation has been completed
        (successfuly or otherwise) and send a notification to the server.
        """
        # If delegated to an external program, we do not report to pdserver
        # that the update is complete.
        if update.delegated:
            return

        out.info("The update is done, report it to server...")
        update_id = update.external['update_id']
        success = update.result.get('success', False)

        request = PDServerRequest('/api/routers/{router_id}/updates/' +
                                  str(update_id))
        d = request.patch(
            {
                'op': 'replace',
                'path': '/completed',
                'value': True
            }, {
                'op': 'replace',
                'path': '/success',
                'value': success
            })

        return d
Exemplo n.º 11
0
 def onConnect(self):
     out.info('Router session connected.')
     if (nexus.core.info.pdid and nexus.core.getKey('apitoken')):
         out.info('Starting WAMP-CRA authentication on realm "{}" as user "{}"...'\
                  .format(self.config.realm, nexus.core.info.pdid))
         self.join(self.config.realm, [u'wampcra'],
                 ensure_unicode(nexus.core.info.pdid))
Exemplo n.º 12
0
    def onStart(self):
        super(Nexus, self).onStart()
        # onStart is called when the reactor starts, not when the connection is made.
        # Check for provisioning keys and attempt to connect
        if not self.provisioned() and not provisioning.can_provision():
            out.warn(
                "The node is not provisioned and is not configured to self-provision."
            )
            return

        while not self.provisioned():
            yield provisioning.provision_self(self.update_manager)

        try:
            # Set up communication with pdserver.
            # 1. Create a report of the current system state and send that.
            # 2. Send the node public key.
            # 3. Poll for a list of updates that should be applied.
            # 4. Open WAMP session.
            yield sendStateReport()
            yield sendNodeIdentity()
            yield self.update_fetcher.start_polling()
            yield self.connect(WampSession)
        except Exception:
            out.warn('The router ID or password is invalid!')
        else:
            out.info('WAMP session is ready!')
Exemplo n.º 13
0
    def execute(self):
        try:
            proc = subprocess.Popen(self.command,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE)
            self.pid = proc.pid
            for line in proc.stdout:
                out.verbose("{} {}: {}".format(self.command[0], self.pid,
                                               line))
            for line in proc.stderr:
                out.verbose("{} {}: {}".format(self.command[0], self.pid,
                                               line))
            self.result = proc.wait()
            if self.result == 0:
                out.verbose('Command "{}" returned {}\n'.format(
                    " ".join(self.command), self.result))
            else:
                out.info('Command "{}" returned {}\n'.format(
                    " ".join(self.command), self.result))
        except Exception as e:
            out.info('Command "{}" raised exception {}\n'.format(
                " ".join(self.command), e))
            self.result = e

        if self.parent is not None:
            self.parent.executed.append(self)

        return (self.ignoreFailure or self.result == 0)
Exemplo n.º 14
0
    def execute(self):
        try:
            proc = subprocess.Popen(self.command, stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
            self.pid = proc.pid
            for line in proc.stdout:
                out.verbose("{} {}: {}".format(self.command[0], self.pid, line))
            for line in proc.stderr:
                out.verbose("{} {}: {}".format(self.command[0], self.pid, line))
            self.result = proc.wait()
            if self.result == 0:
                out.verbose('Command "{}" returned {}\n'.format(
                         " ".join(self.command), self.result))
            else:
                out.info('Command "{}" returned {}\n'.format(
                         " ".join(self.command), self.result))
        except Exception as e:
            out.info('Command "{}" raised exception {}\n'.format(
                     " ".join(self.command), e))
            self.result = e

        if self.parent is not None:
            self.parent.executed.append(self)

        return (self.ignoreFailure or self.result == 0)
Exemplo n.º 15
0
def getVirtPreamble(update):
    """
    Prepare various settings for Docker containers.
    """
    extDataDir = settings.EXTERNAL_DATA_DIR.format(chute=update.new.name)
    extDataDir = os.path.expanduser(extDataDir)
    intDataDir = getattr(update.new, 'dataDir', settings.INTERNAL_DATA_DIR)

    extSystemDir = settings.EXTERNAL_SYSTEM_DIR.format(chute=update.new.name)
    extSystemDir = os.path.expanduser(extSystemDir)
    intSystemDir = getattr(update.new, 'systemDir', settings.INTERNAL_SYSTEM_DIR)

    volumes = {
        extDataDir: {
            'bind': intDataDir,
            'mode': 'rw'
        },

        extSystemDir: {
            'bind': intSystemDir,
            'mode': 'ro'
        }
    }

    # Prepare port bindings while there might still be an old version of the
    # chute running so that we can inherit any dynamic port settings.
    for service in update.new.get_services():
        bindings = dockerapi.prepare_port_bindings(service)
        update.cache_set("portBindings:{}".format(service.name), bindings)

    update.cache_set('volumes', volumes)
    update.cache_set('internalDataDir', intDataDir)
    update.cache_set('externalDataDir', extDataDir)
    update.cache_set('internalSystemDir', intSystemDir)
    update.cache_set('externalSystemDir', extSystemDir)

    # Reuse previous token if it exists. This is not ideal but works for
    # updates that do not recreate the container with updated environment
    # variables. Stop then start is one such sequence where environment
    # variables are not updated.
    token = None
    if update.old is not None:
        token = update.old.getCache('apiToken')
    if token is None:
        token = generateToken()
    update.cache_set('apiToken', token)

    # Deprecated: we set these values in the chute cache here because there is
    # still code that depends on reading this from the chute storage.  This can
    # be removed when the corresponding call to getCache is removed.
    update.new.setCache('externalSystemDir', extSystemDir)
    update.new.setCache('apiToken', token)

    if not hasattr(update, 'dockerfile'):
        return
    if update.dockerfile is None:
        return
    else:
        out.info('Using prexisting dockerfile.\n')
        update.dockerfile = BytesIO(update.dockerfile.encode('utf-8'))
Exemplo n.º 16
0
def start_container(update, service):
    """
    Start running a service in a new container.
    """
    client = docker.DockerClient(base_url="unix://var/run/docker.sock", version='auto')

    container_name = service.get_container_name()
    image_name = service.get_image_name()
    out.info("Attempting to start container {} from image {}\n".format(
        container_name, image_name))

    host_config = build_host_config(update, service)

    # TODO
    # Set environment variables for the new container.
    # PARADROP_ROUTER_ID can be used to change application behavior based on
    # what router it is running on.
    environment = prepare_environment(update, service)

    try:
        container = client.containers.run(detach=True, image=image_name,
                name=container_name, environment=environment, **host_config)
        out.info("Successfully started chute with Id: %s\n" % (str(container.id)))
    except Exception as e:
        raise e

    try:
        network = client.networks.get(update.new.name)
        network.connect(container_name, aliases=[service.name])
    except docker.errors.NotFound:
        out.warn("Bridge network {} not found; connectivity between containers is limited.".format(update.new.name))
Exemplo n.º 17
0
def _startChute(chute):
    """
    Create a docker container based on the passed in chute object.
    """
    out.info('Attempting to start new Chute %s \n' % (chute.name))

    repo = getImageName(chute)
    name = chute.name

    c = docker.DockerClient(base_url="unix://var/run/docker.sock",
                            version='auto')

    host_config = build_host_config(chute)

    # Set environment variables for the new container.
    # PARADROP_ROUTER_ID can be used to change application behavior based on
    # what router it is running on.
    environment = prepare_environment(chute)

    try:
        container = c.containers.run(detach=True,
                                     image=repo,
                                     name=name,
                                     environment=environment,
                                     **host_config)
        out.info("Successfully started chute with Id: %s\n" %
                 (str(container.id)))
    except Exception as e:
        raise e

    setup_net_interfaces(chute)
Exemplo n.º 18
0
    def _perform_update(self, update):
        """
        Perform a single update, to be called by perform_updates.

        This is split from perform_updates for easier unit testing.
        """
        try:
            # Mark update as having been started.
            update.started()
            out.info('Performing update %s\n' % (update))

            # TESTING start
            # TODO: still need this?
            if(settings.FC_BOUNCE_UPDATE): # pragma: no cover
                out.testing('Bouncing update %s, result: %s\n' % (
                    update, settings.FC_BOUNCE_UPDATE))
                update.complete(success=True, message=settings.FC_BOUNCE_UPDATE)
                return
            # TESTING end

            # Based on each update type execute could be different
            update.execute()

        except Exception as e:
            out.exception(e, True)
Exemplo n.º 19
0
def getVirtPreamble(update):
    """
    Prepare various settings for Docker containers.
    """
    extDataDir = settings.EXTERNAL_DATA_DIR.format(chute=update.new.name)
    extDataDir = os.path.expanduser(extDataDir)
    intDataDir = getattr(update.new, 'dataDir', settings.INTERNAL_DATA_DIR)

    extSystemDir = settings.EXTERNAL_SYSTEM_DIR.format(chute=update.new.name)
    extSystemDir = os.path.expanduser(extSystemDir)
    intSystemDir = getattr(update.new, 'systemDir',
                           settings.INTERNAL_SYSTEM_DIR)

    volumes = {
        extDataDir: {
            'bind': intDataDir,
            'mode': 'rw'
        },
        extSystemDir: {
            'bind': intSystemDir,
            'mode': 'ro'
        }
    }

    # Prepare port bindings while there might still be an old version of the
    # chute running so that we can inherit any dynamic port settings.
    for service in update.new.get_services():
        bindings = dockerapi.prepare_port_bindings(service)
        update.cache_set("portBindings:{}".format(service.name), bindings)

    update.cache_set('volumes', volumes)
    update.cache_set('internalDataDir', intDataDir)
    update.cache_set('externalDataDir', extDataDir)
    update.cache_set('internalSystemDir', intSystemDir)
    update.cache_set('externalSystemDir', extSystemDir)

    # Reuse previous token if it exists. This is not ideal but works for
    # updates that do not recreate the container with updated environment
    # variables. Stop then start is one such sequence where environment
    # variables are not updated.
    token = None
    if update.old is not None:
        token = update.old.getCache('apiToken')
    if token is None:
        token = generateToken()
    update.cache_set('apiToken', token)

    # Deprecated: we set these values in the chute cache here because there is
    # still code that depends on reading this from the chute storage.  This can
    # be removed when the corresponding call to getCache is removed.
    update.new.setCache('externalSystemDir', extSystemDir)
    update.new.setCache('apiToken', token)

    if not hasattr(update, 'dockerfile'):
        return
    if update.dockerfile is None:
        return
    else:
        out.info('Using prexisting dockerfile.\n')
        update.dockerfile = BytesIO(update.dockerfile.encode('utf-8'))
Exemplo n.º 20
0
    def onStop(self):
        if self.session is not None:
            self.session.leave()
        else:
            out.info('No WAMP session found!')

        super(Nexus, self).onStop()
Exemplo n.º 21
0
 def childDataReceived(self, childFd, data):
     if (childFd == 4):
         # Output of airshark analyzer
         #out.info(data)
         self.airshark_manager.on_analyzer_message(data)
     elif (childFd == 1 or childFd == 2):
         # stdout/stderr of airshark analyzer
         out.info('Airshark: ========\n%s==================' % data)
Exemplo n.º 22
0
 def decorated(self, request, *args, **kwargs):
     if not check_auth(request, self.password_manager, self.token_manager):
         out.info('HTTP {} {} {} {}'.format(request.getClientIP(),
             request.method, request.path, 401))
         request.setResponseCode(401)
         request.setHeader("WWW-Authenticate", "Basic realm=\"Login Required\"")
         return
     return func(self, request, *args, **kwargs)
Exemplo n.º 23
0
    def connectionLost(self, reason):
        if self.transport in self.factory.transports:
            self.factory.transports.remove(self.transport)
        out.info('sockjs /logs disconnected')

        self.loop.stop()
        self.log_provider.detach()
        self.log_provider = None
Exemplo n.º 24
0
 def decorated(self, request, *args, **kwargs):
     if not check_auth(request, self.password_manager, self.token_manager):
         out.info('HTTP {} {} {} {}'.format(request.getClientIP(),
             request.method, request.path, 401))
         request.setResponseCode(401)
         request.setHeader("WWW-Authenticate", "Basic realm=\"Login Required\"")
         return
     return func(self, request, *args, **kwargs)
Exemplo n.º 25
0
def buildImage(update):
    """
    Build the Docker image and monitor progress.
    """
    out.info('Building image for {}\n'.format(update.new))

    client = docker.APIClient(base_url="unix://var/run/docker.sock",
                              version='auto')

    repo = getImageName(update.new)

    if hasattr(update.new, 'external_image'):
        # If the pull fails, we will fall through and attempt a local build.
        # Be aware, in that case, the image will be tagged as if it came from
        # the registry (e.g. registry.exis.io/image) but will have a different
        # image id from the published version.  The build should be effectively
        # the same, though.
        pulled = _pullImage(update, client)
        if pulled:
            return None
        else:
            update.progress("Pull failed, attempting a local build.")

    if hasattr(update, 'dockerfile'):
        buildSuccess = _buildImage(update,
                                   client,
                                   True,
                                   rm=True,
                                   tag=repo,
                                   fileobj=update.dockerfile)
    elif hasattr(update, 'download'):
        # download field should be an object with at least 'url' but may also
        # contain 'user' and 'secret' for authentication.
        download_args = update.download
        with downloader(**download_args) as dl:
            workDir, meta = dl.fetch()
            buildSuccess = _buildImage(update,
                                       client,
                                       False,
                                       rm=True,
                                       tag=repo,
                                       path=workDir)
    elif hasattr(update, 'workdir'):
        buildSuccess = _buildImage(update,
                                   client,
                                   False,
                                   rm=True,
                                   tag=repo,
                                   path=update.workdir)
    else:
        raise Exception("No Dockerfile or download location supplied.")

    #If we failed to build skip creating and starting clean up and fail
    if not buildSuccess:
        raise Exception(
            "Building docker image failed; check your Dockerfile for errors.")
Exemplo n.º 26
0
def wait_for_zerotier(max_delay=120):
    """
    Wait for ZeroTier to start up and create the authtoken file.
    """
    path = os.path.join(settings.ZEROTIER_LIB_DIR, "authtoken.secret")

    if not os.path.isfile(path):
        out.info("Waiting up to {} seconds for ZeroTier authtoken\n".format(max_delay))

    while not os.path.isfile(path):
        time.sleep(1)
Exemplo n.º 27
0
def wait_for_zerotier(max_delay=120):
    """
    Wait for ZeroTier to start up and create the authtoken file.
    """
    path = os.path.join(settings.ZEROTIER_LIB_DIR, "authtoken.secret")

    if not os.path.isfile(path):
        out.info("Waiting up to {} seconds for ZeroTier authtoken\n".format(max_delay))

    while not os.path.isfile(path):
        time.sleep(1)
Exemplo n.º 28
0
def restartChute(update):
    """
    Start a docker container based on the passed in update.

    :param update: The update object containing information about the chute.
    :type update: obj
    :returns: None
    """
    out.info('Attempting to restart chute %s\n' % (update.name))
    c = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
    container = c.containers.get(update.name)
    container.start()
Exemplo n.º 29
0
    def _start(self):
        self.scanner.cmd_chanscan()
        self.scanner.start()

        if not self.analyzer_process.isRunning():
            out.info("Launching airshark analyzer")
            cmd = [settings.AIRSHARK_INSTALL_DIR + "/analyzer", settings.AIRSHARK_INSTALL_DIR + "/specshape/specshape_v1_722N_5m.txt", "--spectrum-fd=3", "--output-fd=4"]
            spawnProcess(self.analyzer_process,\
                         cmd[0], cmd, env=None, \
                         childFDs={0:"w", 1:"r", 2:2, 3:"w", 4:"r"})

        self.loop.start(0.2)
        return True
Exemplo n.º 30
0
    def _start(self):
        self.scanner.cmd_chanscan()
        self.scanner.start()

        if not self.analyzer_process.isRunning():
            out.info("Launching airshark analyzer")
            cmd = [settings.AIRSHARK_INSTALL_DIR + "/analyzer", settings.AIRSHARK_INSTALL_DIR + "/specshape/specshape_v1_722N_5m.txt", "--spectrum-fd=3", "--output-fd=4"]
            spawnProcess(self.analyzer_process,\
                         cmd[0], cmd, env=None, \
                         childFDs={0:"w", 1:"r", 2:2, 3:"w", 4:"r"})

        self.loop.start(0.2)
        return True
Exemplo n.º 31
0
def _removeImage(chute):
    """
    Remove the image for a chute.
    """
    image = getImageName(chute)
    out.info("Removing image {}\n".format(image))

    try:
        client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                                     version='auto')
        client.images.remove(image=image)
    except Exception as error:
        out.warn("Error removing image: {}".format(error))
Exemplo n.º 32
0
def restartChute(update):
    """
    Start a docker container based on the passed in update.

    :param update: The update object containing information about the chute.
    :type update: obj
    :returns: None
    """
    out.info('Attempting to restart chute %s\n' % (update.name))
    c = docker.DockerClient(base_url='unix://var/run/docker.sock',
                            version='auto')
    container = c.containers.get(update.name)
    container.start()
Exemplo n.º 33
0
def remove_image(update, service):
    """
    Remove a Docker image.
    """
    client = docker.APIClient(base_url="unix://var/run/docker.sock", version='auto')

    image_name = service.get_image_name()
    out.info("Removing image {}\n".format(image_name))

    try:
        client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                version='auto')
        client.images.remove(image=image_name)
    except Exception as error:
        out.warn("Error removing image: {}".format(error))
Exemplo n.º 34
0
    def _perform_update(self, update):
        """
        Perform a single update, to be called by perform_updates.

        This is split from perform_updates for easier unit testing.
        """
        # Add to the active set when processing starts. It is a dictionary, so
        # it does not matter if this is the first time we see this update or
        # if we are resuming it.
        self.active_changes[update.change_id] = update

        try:
            # Mark update as having been started.
            update.started()
            out.info('Performing update %s\n' % (update))

            # TESTING start
            # TODO: still need this?
            if (settings.FC_BOUNCE_UPDATE):  # pragma: no cover
                out.testing('Bouncing update %s, result: %s\n' %
                            (update, settings.FC_BOUNCE_UPDATE))
                update.complete(success=True,
                                message=settings.FC_BOUNCE_UPDATE)
                return
            # TESTING end

            # Based on each update type execute could be different
            result = update.execute()
            if isinstance(result, defer.Deferred):
                # Update is not done, but it is yielding. When the deferred
                # fires, add it back to the work queue to resume it.
                #
                # TODO: We could handle errors separately and use that to break
                # the update pipeline, but then we need to propagate that
                # through the update.execute, executionplan chain.  For now,
                # we have an update stage right after prepare_image that checks
                # if the build was successful or throws an exception. That
                # should work but is not very general.
                def resume(result):
                    self.updateQueue.append(update)

                result.addBoth(resume)
            elif update.change_id in self.active_changes:
                # Update is done, so remove it from the active list.
                del self.active_changes[update.change_id]

        except Exception as e:
            out.exception(e, True)
Exemplo n.º 35
0
def getVirtPreamble(update):
    extDataDir = settings.EXTERNAL_DATA_DIR.format(chute=update.new.name)
    intDataDir = getattr(update.new, 'dataDir', settings.INTERNAL_DATA_DIR)

    extSystemDir = settings.EXTERNAL_SYSTEM_DIR.format(chute=update.new.name)
    intSystemDir = getattr(update.new, 'systemDir',
                           settings.INTERNAL_SYSTEM_DIR)

    volumes = {
        extDataDir: {
            'bind': intDataDir,
            'mode': 'rw'
        },
        extSystemDir: {
            'bind': intSystemDir,
            'mode': 'ro'
        }
    }

    # Prepare port bindings while there might still be an old version of the
    # chute running so that we can inherit any dynamic port settings.
    bindings = dockerapi.prepare_port_bindings(update.new)

    update.new.setCache('volumes', volumes)
    update.new.setCache('internalDataDir', intDataDir)
    update.new.setCache('externalDataDir', extDataDir)
    update.new.setCache('internalSystemDir', intSystemDir)
    update.new.setCache('externalSystemDir', extSystemDir)
    update.new.setCache('portBindings', bindings)

    # Reuse previous token if it exists. This is not ideal but works for
    # updates that do not recreate the container with updated environment
    # variables. Stop then start is one such sequence where environment
    # variables are not updated.
    token = None
    if update.old is not None:
        token = update.old.getCache('apiToken')
    if token is None:
        token = generateToken()
    update.new.setCache('apiToken', token)

    if not hasattr(update, 'dockerfile'):
        return
    if update.dockerfile is None:
        return
    else:
        out.info('Using prexisting dockerfile.\n')
        update.dockerfile = BytesIO(update.dockerfile.encode('utf-8'))
Exemplo n.º 36
0
def setConfig(chute, old, cacheKeys, filepath):
    """
    Helper function used to modify config file of each various setting in /etc/config/
    Returns:
        True: if it modified a file
        False: if it did NOT cause any modifications
    Raises exception if an error occurs.
    """
    # First pull out all the cache keys from the @new chute
    newconfigs = []
    for c in cacheKeys:
        t = chute.getCache(c)
        if (t):
            newconfigs += t

    if (len(newconfigs) == 0):
        out.info('no settings to add %r\n' % (chute))
        # We are no longer returning because we need to remove the old configs if necessary
        # return False

    # add comment to each config so we can differentiate between different chute specific configs
    for e in newconfigs:
        c, o = e
        c['comment'] = chute.name

    # Get the old configs from the file for this chuteid

    # Find the config file
    cfgFile = uci.UCIConfig(filepath)

    # Get all the configs that existed in the old version
    # Note we are getting the old configs from the etc/config/ file instead of the chute object
    # This is to improve reliability  - sometimes the file isn't changed it should be
    # because we have reset the board, messed with chutes, etc. and the new/old chuteobjects are identical
    oldconfigs = cfgFile.getChuteConfigs(chute.name)

    if (uci.chuteConfigsMatch(oldconfigs, newconfigs)):
        # configs match, skipping reloading
        # Save a backup in case we need to restore.
        cfgFile.backup(backupToken="paradrop")
        return False
    else:
        # We need to make changes so delete old configs, load new configs
        # configs don't match, changing chutes and reloading
        cfgFile.delConfigs(oldconfigs)
        cfgFile.addConfigs(newconfigs)
        cfgFile.save(backupToken="paradrop", internalid=chute.name)
        return True
Exemplo n.º 37
0
    def execute(self):
        pid = self.getPid()

        if pid is None:
            self.result = 0
            return True

        try:
            retval = kill(pid)
            self.result = 0
            out.info('Command "kill {}" returned {}\n'.format(pid, retval))
        except Exception as e:
            out.info('Command "kill {}" raised exception {}\n'.format(pid, e))
            self.result = e

        return (self.result == 0)
Exemplo n.º 38
0
def remove_image(update, service):
    """
    Remove a Docker image.
    """
    client = docker.APIClient(base_url="unix://var/run/docker.sock",
                              version='auto')

    image_name = service.get_image_name()
    out.info("Removing image {}\n".format(image_name))

    try:
        client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                                     version='auto')
        client.images.remove(image=image_name)
    except Exception as error:
        out.warn("Error removing image: {}".format(error))
Exemplo n.º 39
0
    def execute(self):
        pid = self.getPid()

        if pid is None:
            self.result = 0
            return True

        try:
            retval = kill(pid)
            self.result = 0
            out.info('Command "kill {}" returned {}\n'.format(pid, retval))
        except Exception as e:
            out.info('Command "kill {}" raised exception {}\n'.format(pid, e))
            self.result = e

        return (self.result == 0)
Exemplo n.º 40
0
def stopChute(update):
    """
    Stop a docker container based on the passed in update.

    :param update: The update object containing information about the chute.
    :type update: obj
    :returns: None
    """
    out.info('Attempting to stop chute %s\n' % (update.name))

    cleanup_net_interfaces(update.old)

    c = docker.DockerClient(base_url='unix://var/run/docker.sock',
                            version='auto')
    container = c.containers.get(update.name)
    container.stop()
Exemplo n.º 41
0
    def _perform_update(self, update):
        """
        Perform a single update, to be called by perform_updates.

        This is split from perform_updates for easier unit testing.
        """
        # Add to the active set when processing starts. It is a dictionary, so
        # it does not matter if this is the first time we see this update or
        # if we are resuming it.
        self.active_changes[update.change_id] = update

        try:
            # Mark update as having been started.
            update.started()
            out.info('Performing update %s\n' % (update))

            # TESTING start
            # TODO: still need this?
            if(settings.FC_BOUNCE_UPDATE): # pragma: no cover
                out.testing('Bouncing update %s, result: %s\n' % (
                    update, settings.FC_BOUNCE_UPDATE))
                update.complete(success=True, message=settings.FC_BOUNCE_UPDATE)
                return
            # TESTING end

            # Based on each update type execute could be different
            result = update.execute()
            if isinstance(result, defer.Deferred):
                # Update is not done, but it is yielding. When the deferred
                # fires, add it back to the work queue to resume it.
                #
                # TODO: We could handle errors separately and use that to break
                # the update pipeline, but then we need to propagate that
                # through the update.execute, executionplan chain.  For now,
                # we have an update stage right after prepare_image that checks
                # if the build was successful or throws an exception. That
                # should work but is not very general.
                def resume(result):
                    self.updateQueue.append(update)
                result.addBoth(resume)
            elif update.change_id in self.active_changes:
                # Update is done, so remove it from the active list.
                del self.active_changes[update.change_id]

        except Exception as e:
            out.exception(e, True)
Exemplo n.º 42
0
def remove_container(update, service):
    """
    Remove a service's container.
    """
    container_name = service.get_container_name()
    out.info("Removing container {}\n".format(container_name))

    try:
        client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                version='auto')

        # Grab the last 40 log messages to help with debugging.
        container = client.containers.get(container_name)
        logs = container.logs(stream=False, tail=40, timestamps=False)
        update.progress("{}: {}".format(container_name, logs.rstrip()))

        container.remove(force=True)
    except Exception as error:
        out.warn("Error removing container: {}".format(error))
Exemplo n.º 43
0
def remove_container(update, service):
    """
    Remove a service's container.
    """
    container_name = service.get_container_name()
    out.info("Removing container {}\n".format(container_name))

    try:
        client = docker.DockerClient(base_url="unix://var/run/docker.sock",
                                     version='auto')

        # Grab the last 40 log messages to help with debugging.
        container = client.containers.get(container_name)
        logs = container.logs(stream=False, tail=40, timestamps=False)
        update.progress("{}: {}".format(container_name, logs.rstrip()))

        container.remove(force=True)
    except Exception as error:
        out.warn("Error removing container: {}".format(error))
Exemplo n.º 44
0
    def saveToDisk(self):
        """Saves the data to disk."""
        out.info('Saving to disk (%s)\n' % (self.filename))

        # Make sure they want to save
        if (not self.attrSaveable()):
            return

        # Get whatever the data is
        pyld = self.exportAttr(self.getAttr())

        # Write the file to disk, truncate if it exists
        try:
            with open(self.filename, 'wb') as output:
                pickle.dump(pyld, output)
                os.fsync(output.fileno())

        except Exception as e:
            out.err('Error writing to disk %s\n' % (str(e)))
Exemplo n.º 45
0
def removeOldContainer(update):
    """
    Remove the docker container for the old version of a chute.

    :param update: The update object containing information about the chute.
    :type update: obj
    :returns: None
    """
    out.info('Attempting to remove chute %s\n' % (update.name))

    cleanup_net_interfaces(update.old)

    client = docker.DockerClient(base_url='unix://var/run/docker.sock',
                                 version='auto')
    try:
        container = client.containers.get(update.old.name)
        container.remove(force=True)
    except Exception as e:
        update.progress(str(e))
Exemplo n.º 46
0
    def _maybeWait(self, result):
        """
        Wait for completed result if configured to wait on async calls.
        """
        if result['type'] == 'sync' or not self.wait_async:
            return result['result']

        if result['type'] == 'error':
            result = result['result']
            if self.logging:
                out.warn("snapd error: {}".format(result['message']))
            return result

        while True:
            change = self.get_change(result['change'])
            if change['ready']:
                if self.logging:
                    out.info("{}: {}".format(change['summary'], change['status']))
                return change
            time.sleep(1)
Exemplo n.º 47
0
    def saveToDisk(self):
        """Saves the data to disk."""
        out.info('Saving to disk (%s)\n' % (self.filename))

        # Make sure they want to save
        if(not self.attrSaveable()):
            return

        # Get whatever the data is
        pyld = self.exportAttr(self.getAttr())

        # Write the file to disk, truncate if it exists
        try:
            with open(self.filename, 'wb') as output:
                pickle.dump(pyld, output)
                os.fsync(output.fileno())
        except Exception as e:
            out.err('Error writing to disk %s\n' % (str(e)))

        try:
            with open(self.filename + ".yaml", "w") as output:
                yaml.dump(pyld, output)
        except Exception as error:
            out.err("Error writing yaml file: {}".format(error))
Exemplo n.º 48
0
def call_retry(cmd, env, delay=3, tries=3):
    # Make sure each component of the command is a string.  Otherwisew we will
    # get errors.
    clean_cmd = [str(v) for v in cmd]

    while tries >= 0:
        tries -= 1

        out.info("Calling: {}\n".format(" ".join(clean_cmd)))
        try:
            proc = subprocess.Popen(clean_cmd, stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE, env=env)
            for line in proc.stdout:
                out.info("{}: {}\n".format(clean_cmd[0], line.strip()))
            for line in proc.stderr:
                out.warn("{}: {}\n".format(clean_cmd[0], line.strip()))
            return proc.returncode
        except OSError as e:
            out.warn('Command "{}" failed\n'.format(" ".join(clean_cmd)))
            if tries <= 0:
                out.exception(e, True)
                raise e

        time.sleep(delay)
Exemplo n.º 49
0
 def onLeave(self, details):
     out.info("Router session left: {}".format(details))
     nexus.core.wamp_connected = False
     self.disconnect()
Exemplo n.º 50
0
 def onClose(self, wasClean, code, reason):
     out.info('ws /airshark/analyzer disconnected: {}'.format(reason))
     self.factory.airshark_manager.remove_analyzer_observer(self)
Exemplo n.º 51
0
 def onOpen(self):
     out.info('ws /airshark/analyzer connected')
     self.factory.airshark_manager.add_analyzer_observer(self)
Exemplo n.º 52
0
 def onOpen(self):
     out.info('ws /airshark/spectrum connected')
     self.factory.airshark_manager.add_spectrum_observer(self)
Exemplo n.º 53
0
    def save(self, backupToken="paradrop", internalid=None):
        """
            Saves out the file in the proper format.

            Arguments:
                [backupPath] : Save a backup copy of the UCI file to the path provided.
                                Should be a token name like 'backup', it gets appended with a hyphen.
        """
        # Save original copy
        if(backupToken):
            self.backup(backupToken)

        output = ""
        output += "#" * 80 + "\n"
        output += "# Configuration file generated by paradrop-daemon\n".format(self.myname)
        output += "# Path: {}\n".format(self.filepath)
        output += "# Package: {}\n".format(self.myname)
        output += "#" * 80 + "\n"
        output += "\n"

        # Now generate what the file would look like
        for c, o in self.config:
            line = "config %s" % c['type']
            # Check for optional name
            if 'name' in c:
                line += " %s" % c['name']
            if 'comment' in c:
                line += " #%s" % c['comment']
            output += "%s\n" % line

            for k, v in six.iteritems(o):
                if isinstance(v, list):
                    # For list-valued options, emit one line for each item
                    # in the list.
                    for vals in v:
                        # Now append a list set to the config
                        line = "\tlist %s '%s'\n" % (k, vals)
                        output += line

                # Skip options that are None rather than writing "None".
                elif v is not None:
                    sv = stringifyOptionValue(v)
                    line = "\toption %s '%s'\n" % (k, sv)
                    output += line

            # Now add one extra newline before the next set
            output += "\n"

        # Now write to disk
        try:
            out.info('Saving %s to disk\n' % (self.filepath))
            fd = pdos.open(self.filepath, 'w')
            fd.write(output)

            # Guarantee that its written to disk before we close
            fd.flush()
            os.fsync(fd.fileno())
            fd.close()
        except Exception as e:
            out.err('Unable to save new config %s, %s\n' % (self.filepath, str(e)))
            out.err('Config may be corrupted, backup exists at /tmp/%s\n' % (self.myname))
Exemplo n.º 54
0
 def updatesPending(self, pdid):
     out.info('Notified of updates...')
     if WampSession.update_fetcher is not None:
         out.info('Pulling updates from the server...')
         yield WampSession.update_fetcher.pull_update()
Exemplo n.º 55
0
def fulfillDeviceRequest(update, cfg, devices):
    """
    Find a physical device that matches the requested device type.

    Raises an exception if one cannot be found.
    """
    dtype, mode = split_interface_type(cfg['type'])

    # Get list of devices by requested type.
    devlist = devices.get(dtype, [])

    reservations = update.cache_get('deviceReservations')

    bestDevice = None
    bestScore = -1

    for device in devlist:
        dname = device['name']

        # If the configuration object includes a 'requests' object, then look
        # up configuration of the device and check additional constraints, e.g.
        # channel or hwmode.
        if 'requests' in cfg:
            phyconf = get_current_phy_conf(update, device['id'])
            if not satisfies_requirements(phyconf, cfg['requests']):
                continue

        # Monitor, station, and airshark mode interfaces require exclusive
        # access to the device.
        if dtype == "wifi" and mode in ["monitor", "sta", "airshark"]:
            if reservations[dname].count() > 0:
                continue

            # Choose the first one that matches.
            bestDevice = device
            break

        # AP mode interfaces can share a device, but not with monitor mode or
        # station mode.
        elif dtype == "wifi" and mode == "ap":
            if reservations[dname].count(mode="monitor") > 0:
                continue
            if reservations[dname].count(mode="sta") > 0:
                continue

            apcount = reservations[dname].count(mode="ap")

            # Avoid exceeding the max. number of AP interfaces.
            if apcount >= MAX_AP_INTERFACES:
                continue

            # Otherwise, prefer interfaces that have at least one AP already.
            # This preference leaves interfaces available for other purposes
            # (e.g. monitor mode).
            if apcount > bestScore:
                bestDevice = device
                bestScore = apcount

        else:
            # Handle other devices types, namely "lan".  Assume they require
            # exclusive access.
            if reservations[dname].count() > 0:
                continue
            else:
                bestDevice = device
                break

    if bestDevice is not None:
        out.info("Assign device {} for requested type {}".format(
            bestDevice['name'], dtype))
        reservations[bestDevice['name']].add(update.new.name, dtype, mode)
        return bestDevice

    raise Exception(
        "Could not satisfy requirement for device of type {}.".format(dtype))
Exemplo n.º 56
0
 def onDisconnect(self):
     out.info("Router session disconnected.")
Exemplo n.º 57
0
 def onClose(self, wasClean, code, reason):
     out.info('ws /paradrop_logs disconnected: {}'.format(reason))
     self.factory.removeParadropLogObserver(self)
Exemplo n.º 58
0
 def onOpen(self):
     out.info('ws /paradrop_logs connected')
     self.factory.addParadropLogObserver(self)
Exemplo n.º 59
0
    def onJoin(self, details):
        out.info('Router session joined')

        yield self.subscribe(self.updatesPending, self.uriPrefix + 'updatesPending')
        yield self.register(self.update, self.uriPrefix + 'update')
        yield BaseSession.onJoin(self, details)
Exemplo n.º 60
0
 def execute(self):
     out.info("An error occurred: {}".format(self.error))