Esempio n. 1
0
    def do_deploy_async(self, values, sender=None):
        # Do the magic
        #
        # In case of error raise an exception
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Test Role")
        target.add_required_units(['rolekit.socket'])

        yield target
Esempio n. 2
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE: do_deploy_async")
        # Do the magic
        #
        # In case of error raise an exception

        # Get the domain name from the passed-in settings
        # or set it to the instance name if ommitted

        if 'domain_name' not in values:
            values['domain_name'] = self.get_name()

        if not self._valid_fqdn(values['domain_name']):
            raise RolekitError(
                INVALID_VALUE,
                "Invalid domain name: %s" % values['domain_name'])

        if "host_name" not in values:
            # Let's construct a new host name.
            host_part = self._get_hostname()
            if host_part.startswith("localhost"):
                # We'll assign a random hostname starting with "dc-"
                random_part = ''.join(
                    random.choice(string.ascii_lowercase) for _ in range(16))
                host_part = "dc-%s" % random_part

            values['host_name'] = "%s.%s" % (host_part, values['domain_name'])

        if not self._valid_fqdn(values['host_name']):
            raise RolekitError(INVALID_VALUE,
                               "Invalid host name: %s" % values['host_name'])

        # Change the hostname with the hostnamectl API
        yield set_hostname(values['host_name'])

        # If left unspecified, default the realm to the
        # upper-case version of the domain name
        if 'realm_name' not in values:
            values['realm_name'] = values['domain_name'].upper()

        # If left unspecified, assign a random password for
        # the administrative user
        if 'admin_password' not in values:
            admin_pw_provided = False
            values['admin_password'] = generate_password()
        else:
            admin_pw_provided = True

        # If left unspecified, assign a random password for
        # the directory manager
        if 'dm_password' not in values:
            dm_pw_provided = False
            values['dm_password'] = generate_password()
        else:
            dm_pw_provided = True

        # Call ipa-server-install with the requested arguments
        ipa_install_args = [
            'ipa-server-install',
            '-U',
            '-r',
            values['realm_name'],
            '-d',
            values['domain_name'],
            '-p',
            values['dm_password'],
            '-a',
            values['admin_password'],
        ]

        # If the user has requested the DNS server, enable it
        if 'serve_dns' not in values:
            values['serve_dns'] = self._settings['serve_dns']

        if values['serve_dns']:
            ipa_install_args.append('--setup-dns')

            # Pass the primary IP address
            if 'primary_ip' in values:
                ipa_install_args.append('--ip-address=%s' %
                                        values['primary_ip'])

            # if the user has requested DNS forwarders, add them
            if 'dns_forwarders' in values:
                [
                    ipa_install_args.append("--forwarder=%s" % x)
                    for x in values['dns_forwarders']['ipv4']
                ]
                [
                    ipa_install_args.append("--forwarder=%s" % x)
                    for x in values['dns_forwarders']['ipv6']
                ]
            else:
                ipa_install_args.append('--no-forwarders')

            # If the user has requested the reverse zone add it
            if 'reverse_zone' in values:
                for zone in values['reverse_zone']:
                    ipa_install_args.append('--reverse-zone=%s' % zone)
            else:
                ipa_install_args.append('--no-reverse')

        # If the user has requested a specified ID range,
        # set up the argument to ipa-server-install
        if 'id_start' in values or 'id_max' in values:
            if ('id_start' not in values or 'id_max' not in values
                    or not values['id_start'] or not values['id_max']):

                raise RolekitError(
                    INVALID_VALUE, "Must specify id_start and id_max together")

            if (values['id_start'] and values['id_max'] <= values['id_start']):
                raise RolekitError(INVALID_VALUE,
                                   "id_max must be greater than id_start")

            ipa_install_args.append('--idstart=%d' % values['id_start'])
            ipa_install_args.append('--idmax=%d' % values['id_max'])

        # TODO: If the user has specified a root CA file,
        # set up the argument to ipa-server-install

        # Remove the passwords from the values so
        # they won't be saved to the settings
        if admin_pw_provided:
            values.pop('admin_password', None)
        if dm_pw_provided:
            values.pop('dm_password', None)

        result = yield async .subprocess_future(ipa_install_args)

        if result.status:
            # If the subprocess returned non-zero, raise an exception
            raise RolekitError(COMMAND_FAILED, "%d" % result.status)

        # Create the systemd target definition
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Domain Controller")
        target.add_required_units(['ipa.service'])

        # We're done!
        yield target
Esempio n. 3
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE: do_deploy_async")
        # Run whatever series of actions are needed to deploy
        # this role in a meaningful way.
        #
        import docker

        # Get the default cache size
        # Find out how much RAM is available on the system
        if 'cache_size' not in values:
            # Do a late import of psutil. This will only get
            # used during a deployment, so we don't need to
            # have it as a dependency for rolekit itself
            import psutil

            # Get the total number of bytes in local system memory
            total_ram = psutil.virtual_memory().total

            # If 25% of the available memory is less than 1GB, use
            # that for the cache.
            if total_ram / 4 < GiB_SIZE:
                # Set cache_size in MiB
                values['cache_size'] = int(total_ram / 4 / MiB_SIZE)
            else:
                # Cap the default size at 1 GB in MiB
                values['cache_size'] = int(GiB_SIZE / MiB_SIZE)

        # Set defaults
        if "connections" not in values:
            values["connections"] = self._DEFAULTS["connections"]

        if "threads" not in values:
            values["threads"] = self._DEFAULTS["threads"]

        # Create a container for memcached and launch that
        log.debug2("Enabling the Docker container manager")

        # Enable and start the docker service
        enable_units(['docker.service'])

        log.debug2("Starting the Docker container manager")
        with SystemdJobHandler() as job_handler:
            job_path = job_handler.manager.StartUnit("docker.service", "replace")
            job_handler.register_job(job_path)

            job_results = yield job_handler.all_jobs_done_future()
            if any([x for x in job_results.values() if x not in ("skipped", "done")]):
                details = ", ".join(["%s: %s" % item for item in job_results.items()])
                raise RolekitError(COMMAND_FAILED, "Starting docker.service failed: %s" % details)

        log.debug2("Pulling %s image from Docker Hub" % MEMCACHED_DOCKER_IMAGE)
        dockerclient = docker.Client(base_url=docker.utils.utils.DEFAULT_UNIX_SOCKET,
                                     version='auto')

        # First, pull down the latest version of the memcached container
        dockerclient.pull(MEMCACHED_DOCKER_IMAGE, tag="latest")

        log.debug2("Creating systemd service unit")
        # Generate a systemd service unit for this container
        container_unit = SystemdContainerServiceUnit(
            image_name = MEMCACHED_DOCKER_IMAGE,
            container_name = "memcached_%s" % self.get_name(),
            desc="memcached docker container - %s" % self.get_name(),
            env = {
                "MEMCACHED_CACHE_SIZE": str(values['cache_size']),
                "MEMCACHED_CONNECTIONS": str(values['connections']),
                "MEMCACHED_THREADS": str(values['threads'])
            },
            ports = ("{0}:{0}/tcp".format(MEMCACHED_DEFAULT_PORT),
                     "{0}:{0}/udp".format(MEMCACHED_DEFAULT_PORT))
        )
        container_unit.write()

        # Make systemd load this new unit file
        log.debug2("Running systemd daemon-reload")
        with SystemdJobHandler() as job_handler:
            job_handler.manager.Reload()

        # Return the target information
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Memory Cache")
        target.add_required_units(['memcached_%s.service' % self.get_name()])

        log.debug9("TRACE: exiting do_deploy_async")
        yield target
Esempio n. 4
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE do_deploy_async(databaseserver)")
        # Do the magic
        #
        # In case of error raise an exception

        first_instance = True

        # Check whether this is the first instance of the database
        for value in self._parent.get_instances().values():
            if ('databaseserver' == value.get_type()
                    and self.get_name() != value.get_name()
                    and self.get_state() in deployed_states):
                first_instance = False
                break

        # If the database name wasn't specified
        if 'database' not in values:
            # Use the instance name if it was manually specified
            if self.get_name()[0].isalpha():
                values['database'] = self.get_name()
            else:
                # Either it was autogenerated or begins with a
                # non-alphabetic character; prefix it with db_
                values['database'] = "db_%s" % self.get_name()

        if 'owner' not in values:
            # We'll default to db_owner
            values['owner'] = "db_owner"

        # We will assume the owner is new until adding them fails
        new_owner = True

        # Determine if a password was passed in, so we know whether to
        # suppress it from the settings list later.
        if 'password' in values:
            password_provided = True
        else:
            password_provided = False

        if 'postgresql_conf' not in values:
            values['postgresql_conf'] = self._settings['postgresql_conf']

        if 'pg_hba_conf' not in values:
            values['pg_hba_conf'] = self._settings['pg_hba_conf']

        # Get the UID and GID of the 'postgres' user
        try:
            self.pg_uid = pwd.getpwnam('postgres').pw_uid
        except KeyError:
            raise RolekitError(MISSING_ID,
                               "Could not retrieve UID for postgres user")

        try:
            self.pg_gid = grp.getgrnam('postgres').gr_gid
        except KeyError:
            raise RolekitError(MISSING_ID,
                               "Could not retrieve GID for postgres group")

        if first_instance:
            # Initialize the database on the filesystem
            initdb_args = ["/usr/bin/postgresql-setup", "--initdb"]

            log.debug2("TRACE: Initializing database")
            result = yield async .subprocess_future(initdb_args)
            if result.status:
                # If this fails, it may be just that the filesystem
                # has already been initialized. We'll log the message
                # and continue.
                log.debug1("INITDB: %s" % result.stdout)

        # Now we have to start the service to set everything else up
        # It's safe to start an already-running service, so we'll
        # just always make this call, particularly in case other instances
        # exist but aren't running.
        log.debug2("TRACE: Starting postgresql.service unit")
        try:
            with SystemdJobHandler() as job_handler:
                job_path = job_handler.manager.StartUnit(
                    "postgresql.service", "replace")
                job_handler.register_job(job_path)
                log.debug2("TRACE: unit start job registered")

                job_results = yield job_handler.all_jobs_done_future()

                log.debug2("TRACE: unit start job concluded")

                if any([
                        x for x in job_results.values()
                        if x not in ("skipped", "done")
                ]):
                    details = ", ".join(
                        ["%s: %s" % item for item in job_results.items()])
                    log.error("Starting services failed: {}".format(details))
                    raise RolekitError(
                        COMMAND_FAILED,
                        "Starting services failed: %s" % details)
        except Exception as e:
            log.error("Error received starting unit: {}".format(e))
            raise

        # Next we create the owner
        log.debug2("TRACE: Creating owner of new database")
        createuser_args = ["/usr/bin/createuser", values['owner']]
        result = yield async .subprocess_future(createuser_args,
                                                uid=self.pg_uid,
                                                gid=self.pg_gid)

        if result.status:
            # If the subprocess returned non-zero, the user probably already exists
            # (such as when we're using db_owner). If the caller was trying to set
            # a password, they probably didn't realize this, so we need to throw
            # an exception.
            log.info1("User {} already exists in the database".format(
                values['owner']))

            if password_provided:
                raise RolekitError(INVALID_SETTING,
                                   "Cannot set password on pre-existing user")

            # If no password was specified, we'll continue
            new_owner = False

        # If no password was requested, generate a random one here
        if not password_provided:
            values['password'] = generate_password()

        log.debug2("TRACE: Creating new database")
        createdb_args = [
            "/usr/bin/createdb", values['database'], "-O", values['owner']
        ]
        result = yield async .subprocess_future(createdb_args,
                                                uid=self.pg_uid,
                                                gid=self.pg_gid)
        if result.status:
            # If the subprocess returned non-zero, raise an exception
            raise RolekitError(COMMAND_FAILED,
                               "Creating database failed: %d" % result.status)

        # Next, set the password on the owner
        # We'll skip this phase if the the user already existed
        if new_owner:
            log.debug2("TRACE: Setting password for database owner")
            pwd_args = [
                ROLEKIT_ROLES + "/databaseserver/tools/rk_db_setpwd.py",
                "--database", values['database'], "--user", values['owner']
            ]
            result = yield async .subprocess_future(pwd_args,
                                                    stdin=values['password'],
                                                    uid=self.pg_uid,
                                                    gid=self.pg_gid)

            if result.status:
                # If the subprocess returned non-zero, raise an exception
                log.error("Setting owner password failed: {}".format(
                    result.status))
                raise RolekitError(
                    COMMAND_FAILED,
                    "Setting owner password failed: %d" % result.status)

            # If this password was provided by the user, don't save it to
            # the settings for later retrieval. That could be a security
            # issue
            if password_provided:
                values.pop("password", None)
        else:  # Not a new owner
            # Never save the password to settings for an existing owner
            log.debug2("TRACE: Owner already exists, not setting password")
            values.pop("password", None)

        if first_instance:
            # Then update the server configuration to accept network
            # connections.
            log.debug2("TRACE: Opening access to external addresses")

            # edit postgresql.conf to add listen_addresses = '*'
            conffile = values['postgresql_conf']
            bakfile = conffile + ".rksave"

            try:
                linkfile(conffile, bakfile)

                with open(conffile) as f:
                    conflines = f.readlines()

                tweaking_rules = [{
                    'regex': r"^\s*#?\s*listen_addresses\s*=.*",
                    'replace': r"listen_addresses = '*'",
                    'append_if_missing': True
                }]

                overwrite_safely(
                    conffile, "".join(_tweak_lines(conflines, tweaking_rules)))
            except Exception as e:
                log.fatal("Couldn't write {!r}: {}".format(conffile, e))
                # At this point, conffile is unmodified, otherwise
                # overwrite_safely() would have succeeded
                try:
                    os.unlink(bakfile)
                except Exception as x:
                    if not (isinstance(x, OSError)
                            and x.errno == errno.ENOENT):
                        log.error("Couldn't remove {!r}: {}".format(
                            bakfile, x))

                raise RolekitError(
                    COMMAND_FAILED,
                    "Opening access to external addresses in '{}'"
                    "failed: {}".format(conffile, e))

            # Edit pg_hba.conf to allow 'md5' auth on IPv4 and
            # IPv6 interfaces.
            conffile = values['pg_hba_conf']
            bakfile = conffile + ".rksave"

            try:
                linkfile(conffile, bakfile)

                with open(conffile) as f:
                    conflines = f.readlines()

                tweaking_rules = [{
                    'regex': r"^\s*host((?:\s.*)$)",
                    'replace': r"#host\1"
                }, {
                    'regex':
                    r"^\s*local(?:\s.*|)$",
                    'append':
                    "# Use md5 method for all connections\nhost    all             all             all                     md5"
                }]

                overwrite_safely(
                    conffile, "".join(_tweak_lines(conflines, tweaking_rules)))
            except Exception as e:
                log.fatal("Couldn't write {!r}: {}".format(conffile, e))
                # At this point, conffile is unmodified, otherwise
                # overwrite_safely() would have succeeded
                try:
                    os.unlink(bakfile)
                except Exception as x:
                    if not (isinstance(x, OSError)
                            and x.errno == errno.ENOENT):
                        log.error("Couldn't remove {!r}: {}".format(
                            bakfile, x))

                # Restore previous postgresql.conf from the backup
                conffile = values['postgresql_conf']
                bakfile = conffile + ".rksave"
                try:
                    os.rename(bakfile, conffile)
                except Exception as x:
                    log.error(
                        "Couldn't restore {!r} from backup {!r}: {}".format(
                            conffile, bakfile, x))

                raise RolekitError(
                    COMMAND_FAILED,
                    "Changing all connections to use md5 method in '{}'"
                    "failed: {}".format(values['pg_hba_conf'], e))

            # Restart the postgresql server to accept the new configuration
            log.debug2("TRACE: Restarting postgresql.service unit")
            with SystemdJobHandler() as job_handler:
                job_path = job_handler.manager.RestartUnit(
                    "postgresql.service", "replace")
                job_handler.register_job(job_path)

                job_results = yield job_handler.all_jobs_done_future()
                if any([
                        x for x in job_results.values()
                        if x not in ("skipped", "done")
                ]):
                    details = ", ".join(
                        ["%s: %s" % item for item in job_results.items()])
                    raise RolekitError(
                        COMMAND_FAILED,
                        "Restarting service failed: %s" % details)

        # Create the systemd target definition
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Database Server")
        target.add_required_units(['postgresql.service'])

        log.debug2("TRACE: Database server deployed")

        yield target
Esempio n. 5
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE do_deploy_async(databaseserver)")
        # Do the magic
        #
        # In case of error raise an exception

        first_instance = True

        # Check whether this is the first instance of the database
        for value in self._parent.get_instances().values():
            if (
                "databaseserver" == value.get_type()
                and self.get_name() != value.get_name()
                and self.get_state() in deployed_states
            ):
                first_instance = False
                break

        # If the database name wasn't specified
        if "database" not in values:
            # Use the instance name if it was manually specified
            if self.get_name()[0].isalpha():
                values["database"] = self.get_name()
            else:
                # Either it was autogenerated or begins with a
                # non-alphabetic character; prefix it with db_
                values["database"] = "db_%s" % self.get_name()

        if "owner" not in values:
            # We'll default to db_owner
            values["owner"] = "db_owner"

        # We will assume the owner is new until adding them fails
        new_owner = True

        # Determine if a password was passed in, so we know whether to
        # suppress it from the settings list later.
        if "password" in values:
            password_provided = True
        else:
            password_provided = False

        if "postgresql_conf" not in values:
            values["postgresql_conf"] = self._settings["postgresql_conf"]

        if "pg_hba_conf" not in values:
            values["pg_hba_conf"] = self._settings["pg_hba_conf"]

        # Get the UID and GID of the 'postgres' user
        try:
            self.pg_uid = pwd.getpwnam("postgres").pw_uid
        except KeyError:
            raise RolekitError(MISSING_ID, "Could not retrieve UID for postgres user")

        try:
            self.pg_gid = grp.getgrnam("postgres").gr_gid
        except KeyError:
            raise RolekitError(MISSING_ID, "Could not retrieve GID for postgres group")

        if first_instance:
            # Initialize the database on the filesystem
            initdb_args = ["/usr/bin/postgresql-setup", "--initdb"]

            log.debug2("TRACE: Initializing database")
            result = yield async.subprocess_future(initdb_args)
            if result.status:
                # If this fails, it may be just that the filesystem
                # has already been initialized. We'll log the message
                # and continue.
                log.debug1("INITDB: %s" % result.stdout)

        # Now we have to start the service to set everything else up
        # It's safe to start an already-running service, so we'll
        # just always make this call, particularly in case other instances
        # exist but aren't running.
        log.debug2("TRACE: Starting postgresql.service unit")
        try:
            with SystemdJobHandler() as job_handler:
                job_path = job_handler.manager.StartUnit("postgresql.service", "replace")
                job_handler.register_job(job_path)
                log.debug2("TRACE: unit start job registered")

                job_results = yield job_handler.all_jobs_done_future()

                log.debug2("TRACE: unit start job concluded")

                if any([x for x in job_results.values() if x not in ("skipped", "done")]):
                    details = ", ".join(["%s: %s" % item for item in job_results.items()])
                    log.error("Starting services failed: {}".format(details))
                    raise RolekitError(COMMAND_FAILED, "Starting services failed: %s" % details)
        except Exception as e:
            log.error("Error received starting unit: {}".format(e))
            raise

        # Next we create the owner
        log.debug2("TRACE: Creating owner of new database")
        createuser_args = ["/usr/bin/createuser", values["owner"]]
        result = yield async.subprocess_future(createuser_args, uid=self.pg_uid, gid=self.pg_gid)

        if result.status:
            # If the subprocess returned non-zero, the user probably already exists
            # (such as when we're using db_owner). If the caller was trying to set
            # a password, they probably didn't realize this, so we need to throw
            # an exception.
            log.info1("User {} already exists in the database".format(values["owner"]))

            if password_provided:
                raise RolekitError(INVALID_SETTING, "Cannot set password on pre-existing user")

            # If no password was specified, we'll continue
            new_owner = False

        # If no password was requested, generate a random one here
        if not password_provided:
            values["password"] = generate_password()

        log.debug2("TRACE: Creating new database")
        createdb_args = ["/usr/bin/createdb", values["database"], "-O", values["owner"]]
        result = yield async.subprocess_future(createdb_args, uid=self.pg_uid, gid=self.pg_gid)
        if result.status:
            # If the subprocess returned non-zero, raise an exception
            raise RolekitError(COMMAND_FAILED, "Creating database failed: %d" % result.status)

        # Next, set the password on the owner
        # We'll skip this phase if the the user already existed
        if new_owner:
            log.debug2("TRACE: Setting password for database owner")
            pwd_args = [
                ROLEKIT_ROLES + "/databaseserver/tools/rk_db_setpwd.py",
                "--database",
                values["database"],
                "--user",
                values["owner"],
            ]
            result = yield async.subprocess_future(pwd_args, stdin=values["password"], uid=self.pg_uid, gid=self.pg_gid)

            if result.status:
                # If the subprocess returned non-zero, raise an exception
                log.error("Setting owner password failed: {}".format(result.status))
                raise RolekitError(COMMAND_FAILED, "Setting owner password failed: %d" % result.status)

            # If this password was provided by the user, don't save it to
            # the settings for later retrieval. That could be a security
            # issue
            if password_provided:
                values.pop("password", None)
        else:  # Not a new owner
            # Never save the password to settings for an existing owner
            log.debug2("TRACE: Owner already exists, not setting password")
            values.pop("password", None)

        if first_instance:
            # Then update the server configuration to accept network
            # connections.
            log.debug2("TRACE: Opening access to external addresses")

            # edit postgresql.conf to add listen_addresses = '*'
            conffile = values["postgresql_conf"]
            bakfile = conffile + ".rksave"

            try:
                linkfile(conffile, bakfile)

                with open(conffile) as f:
                    conflines = f.readlines()

                tweaking_rules = [
                    {
                        "regex": r"^\s*#?\s*listen_addresses\s*=.*",
                        "replace": r"listen_addresses = '*'",
                        "append_if_missing": True,
                    }
                ]

                overwrite_safely(conffile, "".join(_tweak_lines(conflines, tweaking_rules)))
            except Exception as e:
                log.fatal("Couldn't write {!r}: {}".format(conffile, e))
                # At this point, conffile is unmodified, otherwise
                # overwrite_safely() would have succeeded
                try:
                    os.unlink(bakfile)
                except Exception as x:
                    if not (isinstance(x, OSError) and x.errno == errno.ENOENT):
                        log.error("Couldn't remove {!r}: {}".format(bakfile, x))

                raise RolekitError(
                    COMMAND_FAILED, "Opening access to external addresses in '{}'" "failed: {}".format(conffile, e)
                )

            # Edit pg_hba.conf to allow 'md5' auth on IPv4 and
            # IPv6 interfaces.
            conffile = values["pg_hba_conf"]
            bakfile = conffile + ".rksave"

            try:
                linkfile(conffile, bakfile)

                with open(conffile) as f:
                    conflines = f.readlines()

                tweaking_rules = [
                    {"regex": r"^\s*host((?:\s.*)$)", "replace": r"#host\1"},
                    {
                        "regex": r"^\s*local(?:\s.*|)$",
                        "append": "# Use md5 method for all connections\nhost    all             all             all                     md5",
                    },
                ]

                overwrite_safely(conffile, "".join(_tweak_lines(conflines, tweaking_rules)))
            except Exception as e:
                log.fatal("Couldn't write {!r}: {}".format(conffile, e))
                # At this point, conffile is unmodified, otherwise
                # overwrite_safely() would have succeeded
                try:
                    os.unlink(bakfile)
                except Exception as x:
                    if not (isinstance(x, OSError) and x.errno == errno.ENOENT):
                        log.error("Couldn't remove {!r}: {}".format(bakfile, x))

                # Restore previous postgresql.conf from the backup
                conffile = values["postgresql_conf"]
                bakfile = conffile + ".rksave"
                try:
                    os.rename(bakfile, conffile)
                except Exception as x:
                    log.error("Couldn't restore {!r} from backup {!r}: {}".format(conffile, bakfile, x))

                raise RolekitError(
                    COMMAND_FAILED,
                    "Changing all connections to use md5 method in '{}'" "failed: {}".format(values["pg_hba_conf"], e),
                )

            # Restart the postgresql server to accept the new configuration
            log.debug2("TRACE: Restarting postgresql.service unit")
            with SystemdJobHandler() as job_handler:
                job_path = job_handler.manager.RestartUnit("postgresql.service", "replace")
                job_handler.register_job(job_path)

                job_results = yield job_handler.all_jobs_done_future()
                if any([x for x in job_results.values() if x not in ("skipped", "done")]):
                    details = ", ".join(["%s: %s" % item for item in job_results.items()])
                    raise RolekitError(COMMAND_FAILED, "Restarting service failed: %s" % details)

        # Create the systemd target definition
        target = RoleDeploymentValues(self.get_type(), self.get_name(), "Database Server")
        target.add_required_units(["postgresql.service"])

        log.debug2("TRACE: Database server deployed")

        yield target
Esempio n. 6
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE: do_deploy_async")
        # Run whatever series of actions are needed to deploy
        # this role in a meaningful way.
        #
        import docker

        # Get the default cache size
        # Find out how much RAM is available on the system
        if 'cache_size' not in values:
            # Do a late import of psutil. This will only get
            # used during a deployment, so we don't need to
            # have it as a dependency for rolekit itself
            import psutil

            # Get the total number of bytes in local system memory
            total_ram = psutil.virtual_memory().total

            # If 25% of the available memory is less than 1GB, use
            # that for the cache.
            if total_ram / 4 < GiB_SIZE:
                # Set cache_size in MiB
                values['cache_size'] = int(total_ram / 4 / MiB_SIZE)
            else:
                # Cap the default size at 1 GB in MiB
                values['cache_size'] = int(GiB_SIZE / MiB_SIZE)

        # Set defaults
        if "connections" not in values:
            values["connections"] = self._DEFAULTS["connections"]

        if "threads" not in values:
            values["threads"] = self._DEFAULTS["threads"]

        # Create a container for memcached and launch that
        log.debug2("Enabling the Docker container manager")

        # Enable and start the docker service
        enable_units(['docker.service'])

        log.debug2("Starting the Docker container manager")
        with SystemdJobHandler() as job_handler:
            job_path = job_handler.manager.StartUnit("docker.service",
                                                     "replace")
            job_handler.register_job(job_path)

            job_results = yield job_handler.all_jobs_done_future()
            if any([
                    x for x in job_results.values()
                    if x not in ("skipped", "done")
            ]):
                details = ", ".join(
                    ["%s: %s" % item for item in job_results.items()])
                raise RolekitError(
                    COMMAND_FAILED,
                    "Starting docker.service failed: %s" % details)

        log.debug2("Pulling %s image from Docker Hub" % MEMCACHED_DOCKER_IMAGE)
        dockerclient = docker.Client(
            base_url=docker.utils.utils.DEFAULT_UNIX_SOCKET, version='auto')

        # First, pull down the latest version of the memcached container
        dockerclient.pull(MEMCACHED_DOCKER_IMAGE, tag="latest")

        log.debug2("Creating systemd service unit")
        # Generate a systemd service unit for this container
        container_unit = SystemdContainerServiceUnit(
            image_name=MEMCACHED_DOCKER_IMAGE,
            container_name="memcached_%s" % self.get_name(),
            desc="memcached docker container - %s" % self.get_name(),
            env={
                "MEMCACHED_CACHE_SIZE": str(values['cache_size']),
                "MEMCACHED_CONNECTIONS": str(values['connections']),
                "MEMCACHED_THREADS": str(values['threads'])
            },
            ports=("{0}:{0}/tcp".format(MEMCACHED_DEFAULT_PORT),
                   "{0}:{0}/udp".format(MEMCACHED_DEFAULT_PORT)))
        container_unit.write()

        # Make systemd load this new unit file
        log.debug2("Running systemd daemon-reload")
        with SystemdJobHandler() as job_handler:
            job_handler.manager.Reload()

        # Return the target information
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Memory Cache")
        target.add_required_units(['memcached_%s.service' % self.get_name()])

        log.debug9("TRACE: exiting do_deploy_async")
        yield target
Esempio n. 7
0
    def do_deploy_async(self, values, sender=None):
        log.debug9("TRACE: do_deploy_async")
        # Do the magic
        #
        # In case of error raise an exception

        # Get the domain name from the passed-in settings
        # or set it to the instance name if ommitted

        if 'domain_name' not in values:
            values['domain_name'] = self.get_name()

        if not self._valid_fqdn(values['domain_name']):
            raise RolekitError(INVALID_VALUE,
                               "Invalid domain name: %s" % values['domain_name'])

        if "host_name" not in values:
            # Let's construct a new host name.
            host_part = self._get_hostname()
            if host_part.startswith("localhost"):
                # We'll assign a random hostname starting with "dc-"
                random_part = ''.join(random.choice(string.ascii_lowercase)
                                      for _ in range(16))
                host_part = "dc-%s" % random_part

            values['host_name'] = "%s.%s" % (host_part, values['domain_name'])

        if not self._valid_fqdn(values['host_name']):
            raise RolekitError(INVALID_VALUE,
                               "Invalid host name: %s" % values['host_name'])

        # Change the hostname with the hostnamectl API
        yield set_hostname(values['host_name'])

        # If left unspecified, default the realm to the
        # upper-case version of the domain name
        if 'realm_name' not in values:
            values['realm_name'] = values['domain_name'].upper()

        # If left unspecified, assign a random password for
        # the administrative user
        if 'admin_password' not in values:
            admin_pw_provided = False
            values['admin_password'] = generate_password()
        else:
            admin_pw_provided = True

        # If left unspecified, assign a random password for
        # the directory manager
        if 'dm_password' not in values:
            dm_pw_provided = False
            values['dm_password'] = generate_password()
        else:
            dm_pw_provided = True

        # Call ipa-server-install with the requested arguments
        ipa_install_args = [
            'ipa-server-install', '-U',
                '-r', values['realm_name'],
                '-d', values['domain_name'],
                '-p', values['dm_password'],
                '-a', values['admin_password'],
            ]

        # If the user has requested the DNS server, enable it
        if 'serve_dns' not in values:
            values['serve_dns'] = self._settings['serve_dns']

        if values['serve_dns']:
            ipa_install_args.append('--setup-dns')

            # Pass the primary IP address
            if 'primary_ip' in values:
                ipa_install_args.append('--ip-address=%s' %
                                        values['primary_ip'])

            # if the user has requested DNS forwarders, add them
            if 'dns_forwarders' in values:
                [ipa_install_args.append("--forwarder=%s" % x)
                     for x in values['dns_forwarders']['ipv4']]
                [ipa_install_args.append("--forwarder=%s" % x)
                     for x in values['dns_forwarders']['ipv6']]
            else:
                ipa_install_args.append('--no-forwarders')

            # If the user has requested the reverse zone add it
            if 'reverse_zone' in values:
                for zone in values['reverse_zone']:
                    ipa_install_args.append('--reverse-zone=%s' % zone)
            else:
                ipa_install_args.append('--no-reverse')

        # If the user has requested a specified ID range,
        # set up the argument to ipa-server-install
        if 'id_start' in values or 'id_max' in values:
            if ('id_start' not in values or
                'id_max' not in values or
                not values['id_start'] or
                not values['id_max']):

                raise RolekitError(INVALID_VALUE,
                                   "Must specify id_start and id_max together")

            if (values['id_start'] and values['id_max'] <= values['id_start']):
                raise RolekitError(INVALID_VALUE,
                                   "id_max must be greater than id_start")

            ipa_install_args.append('--idstart=%d' % values['id_start'])
            ipa_install_args.append('--idmax=%d' % values['id_max'])

        # TODO: If the user has specified a root CA file,
        # set up the argument to ipa-server-install

        # Remove the passwords from the values so
        # they won't be saved to the settings
        if admin_pw_provided:
            values.pop('admin_password', None)
        if dm_pw_provided:
            values.pop('dm_password', None)

        result = yield async.subprocess_future(ipa_install_args)

        if result.status:
            # If the subprocess returned non-zero, raise an exception
            raise RolekitError(COMMAND_FAILED, "%d" % result.status)


        # Create the systemd target definition
        target = RoleDeploymentValues(self.get_type(), self.get_name(),
                                      "Domain Controller")
        target.add_required_units(['ipa.service'])

        # We're done!
        yield target