Esempio n. 1
0
    def run(self):
        """Setup directories' watches and start the re-scan ticker.
        """
        # Start idle
        self._is_active = False

        # Setup the watchdog
        watchdog_lease = self.tm_env.watchdogs.create(
            name='svc-{svc_name}'.format(svc_name=self.name),
            timeout='{hb:d}s'.format(hb=_WATCHDOG_TIMEOUT_SEC),
            content='Service %r failed' % self.name
        )

        watch = dirwatch.DirWatcher(self.tm_env.cache_dir)
        watch.on_created = self._on_created
        watch.on_modified = self._on_modified
        watch.on_deleted = self._on_deleted

        # Start the timer
        watchdog_lease.heartbeat()

        while True:
            if watch.wait_for_events(timeout=_HEARTBEAT_SEC):
                watch.process_events(max_events=5)
            else:
                if self._is_active is True:
                    cached_files = glob.glob(
                        os.path.join(self.tm_env.cache_dir, '*')
                    )
                    running_links = glob.glob(
                        os.path.join(self.tm_env.running_dir, '*')
                    )
                    # Calculate the container names from every event file
                    cached_containers = {
                        appcfg.eventfile_unique_name(filename)
                        for filename in cached_files
                    }
                    # Calculate the instance names from every event running
                    # link
                    running_instances = {
                        os.path.basename(linkname)
                        for linkname in running_links
                    }
                    _LOGGER.debug('content of %r and %r: %r <-> %r',
                                  self.tm_env.cache_dir,
                                  self.tm_env.running_dir,
                                  cached_containers,
                                  running_instances)

                else:
                    _LOGGER.info('Still inactive during heartbeat event.')

            watchdog_lease.heartbeat()

        # Graceful shutdown.
        _LOGGER.info('service shutdown.')
        watchdog_lease.remove()
Esempio n. 2
0
    def _synchronize(self):
        """Synchronize cache/ instances with running/ instances.

        We need to revalidate three things:

          - All running instances must have an equivalent entry in cache or be
            terminated.

          - All event files in the cache that do not have running link must be
            started.

          - The cached entry and the running link must be for the same
            container (equal unique name). Otherwise, terminate it.

        """
        cached_files = glob.glob(os.path.join(self.tm_env.cache_dir, '*'))
        running_links = glob.glob(os.path.join(self.tm_env.running_dir, '*'))

        # Calculate the instance names from every event file
        cached_instances = {
            os.path.basename(filename)
            for filename in cached_files
        }
        # Calculate the container names from every event file
        cached_containers = {
            appcfg.eventfile_unique_name(filename)
            for filename in cached_files
        }
        # Calculate the instance names from every event running link
        running_instances = {
            os.path.basename(linkname)
            for linkname in running_links
            if os.path.islink(linkname)
        }
        removed_instances = set()
        added_instances = set()

        _LOGGER.info('running %r', running_instances)
        # If instance is extra, remove the symlink and force svscan to
        # re-evaluate
        for instance_link in running_links:
            # Skip any files and directories (created by svscan). The footprint
            # that defines the app is the symlink:
            # - running/<instance_name> -> apps/<container_name>
            #
            # Iterate over all symlinks, skipping other files and directories.
            if not os.path.islink(instance_link):
                continue

            instance_name = os.path.basename(instance_link)
            _LOGGER.info('checking %r', instance_link)
            if not os.path.exists(instance_link):
                # Broken symlink, something went wrong.
                _LOGGER.warning('broken link %r', instance_link)
                os.unlink(instance_link)
                removed_instances.add(instance_name)
                continue

            elif instance_name not in cached_instances:
                self._terminate(instance_name)
                removed_instances.add(instance_name)

            else:
                # Now check that the link match the intended container
                container_dir = self._resolve_running_link(instance_link)
                container_name = os.path.basename(container_dir)
                if container_name not in cached_containers:
                    self._terminate(instance_name)
                    removed_instances.add(instance_name)

        # For all new apps, read the manifest and configure the app. When all
        # apps are configured, force rescan again.
        for instance_name in cached_instances:
            instance_link = os.path.join(
                self.tm_env.running_dir,
                instance_name
            )
            if self._configure(instance_name):
                added_instances.add(instance_name)

        _LOGGER.debug('End resuld: %r / %r - %r + %r',
                      cached_containers,
                      running_instances,
                      removed_instances,
                      added_instances)

        running_instances -= removed_instances
        running_instances |= added_instances
        _LOGGER.info('running post cleanup: %r', running_instances)
        self._refresh_supervisor(instance_names=running_instances)
Esempio n. 3
0
    def _synchronize(self):
        """Synchronize apps to running/cleanup.

        We need to re-validate three things on startup:

          - All configured apps should have an associated cache entry.
            Otherwise, create a link to cleanup.

          - All configured apps with a cache entry and with a cleanup file
            should be linked to cleanup. Otherwise, link to running.

          - Additional cache entries should be configured to run.

        On restart we need to validate another three things:

          - All configured apps that have a running link should be checked
            if in the cache. If not then terminate the app.

          - All configured apps that have a cleanup link should be left
            alone as this is handled.

          - Additional cache entries should be configured to run.

        On startup run.sh will clear running and cleanup which simplifies
        the logic for us as we can check running/cleanup first. Then check
        startup conditions and finally non-configured apps that are in cache.

        NOTE: a link cannot exist in running and cleanup at the same time.

        """
        # Disable R0912(too-many-branches)
        # pylint: disable=R0912
        configured = {
            os.path.basename(filename)
            for filename in glob.glob(os.path.join(self.tm_env.apps_dir, '*'))
        }
        cached = {
            os.path.basename(filename): appcfg.eventfile_unique_name(filename)
            for filename in glob.glob(os.path.join(self.tm_env.cache_dir, '*'))
        }

        for container in configured:
            appname = appcfg.app_name(container)
            if os.path.exists(os.path.join(self.tm_env.running_dir, appname)):
                # App already running.. check if in cache.
                # No need to check if needs cleanup as that is handled
                if appname not in cached or cached[appname] != container:
                    self._terminate(appname)
                else:
                    _LOGGER.info('Ignoring %s as it is running', appname)

                cached.pop(appname, None)

            elif os.path.exists(os.path.join(self.tm_env.cleanup_dir,
                                             appname)):
                # Already in the process of being cleaned up
                _LOGGER.info('Ignoring %s as it is in cleanup', appname)
                cached.pop(appname, None)

            else:
                needs_cleanup = True
                if appname in cached and cached[appname] == container:
                    data_dir = os.path.join(self.tm_env.apps_dir, container,
                                            'data')
                    for cleanup_file in ['exitinfo', 'aborted', 'oom']:
                        path = os.path.join(data_dir, cleanup_file)
                        if os.path.exists(path):
                            _LOGGER.debug('Found cleanup file %r', path)
                            break
                    else:
                        if self._configure(appname):
                            needs_cleanup = False
                            _LOGGER.debug('Added existing app %r', appname)

                    cached.pop(appname, None)

                if needs_cleanup:
                    fs.symlink_safe(
                        os.path.join(self.tm_env.cleanup_dir, appname),
                        os.path.join(self.tm_env.apps_dir, container))
                    _LOGGER.debug('Removed %r', appname)

        for appname in six.iterkeys(cached):
            if self._configure(appname):
                _LOGGER.debug('Added new app %r', appname)

        self._refresh_supervisor()