コード例 #1
0
ファイル: symlinks.py プロジェクト: ntrrgc/blockwart
    def ask(self, status):
        if 'type' in status.info['needs_fixing']:
            if not status.info['path_info'].exists:
                return _("Doesn't exist.")
            else:
                return "{} {} → {}\n".format(
                    bold(_("type")),
                    status.info['path_info'].desc,
                    _("file"),
                )

        question = ""

        if 'owner' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("owner")),
                status.info['path_info'].owner,
                self.attributes['owner'],
            )

        if 'group' in status.info['needs_fixing']:
            question += "{} {} → {}\n".format(
                bold(_("group")),
                status.info['path_info'].group,
                self.attributes['group'],
            )

        return question.rstrip("\n")
コード例 #2
0
ファイル: symlinks.py プロジェクト: ntrrgc/blockwart
    def fix(self, status):
        if 'type' in status.info['needs_fixing']:
            # fixing the type fixes everything
            if status.info['path_info'].exists:
                LOG.info(_("{node}:{item}: fixing type...").format(
                    item=self.id,
                    node=self.node.name,
                ))
            else:
                LOG.info(_("{node}:{item}: creating...").format(
                    item=self.id,
                    node=self.node.name,
                ))
            self._fix_type(status)
            return

        for fix_type in ('owner', 'group'):
            if fix_type in status.info['needs_fixing']:
                if fix_type == 'group' and \
                        'owner' in status.info['needs_fixing']:
                    # owner and group are fixed with a single chown
                    continue
                LOG.info(_("{node}:{item}: fixing {type}...").format(
                    item=self.id,
                    node=self.node.name,
                    type=fix_type,
                ))
                getattr(self, "_fix_" + fix_type)(status)
コード例 #3
0
ファイル: users.py プロジェクト: ntrrgc/blockwart
    def fix(self, status):
        if status.info['exists']:
            if self.attributes['delete']:
                msg = _("{node}:{item}: deleting...")
            else:
                msg = _("{node}:{item}: updating...")
        else:
            msg = _("{node}:{item}: creating...")
        LOG.info(msg.format(item=self.id, node=self.node.name))

        if self.attributes['delete']:
            self.node.run("userdel {}".format(self.name))
        else:
            self.node.run("{command} "
                "-d {home} "
                "-g {gid} "
                "-G {groups} "
                "-p {password_hash} "
                "-s {shell} "
                "-u {uid} "
                "{username}".format(
                    command="useradd" if not status.info['exists'] else "usermod",
                    home=quote(self.attributes['home']),
                    gid=self.attributes['gid'],
                    groups=quote(",".join(self.attributes['groups'])),
                    password_hash=quote(self.attributes['password_hash']),
                    shell=quote(self.attributes['shell']),
                    uid=self.attributes['uid'],
                    username=self.name,
                )
            )
コード例 #4
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
    def validate_attributes(cls, bundle, item_id, attributes):
        if attributes.get('delete', False):
            for attr in attributes.keys():
                if attr not in ['delete'] + BUILTIN_ITEM_ATTRIBUTES.keys():
                    raise BundleError(_(
                        "{item} from bundle '{bundle}' cannot have other "
                        "attributes besides 'delete'"
                    ).format(item=item_id, bundle=bundle.name))
        if 'content' in attributes and 'source' in attributes:
            raise BundleError(_(
                "{item} from bundle '{bundle}' cannot have both 'content' and 'source'"
            ).format(item=item_id, bundle=bundle.name))

        if (
            attributes.get('content_type', None) == "any" and (
                'content' in attributes or
                'encoding' in attributes or
                'source' in attributes
            )
        ):
            raise BundleError(_(
                "{item} from bundle '{bundle}' with content_type 'any' "
                "must not define 'content', 'encoding' and/or 'source'"
            ).format(item=item_id, bundle=bundle.name))

        for key, value in attributes.items():
            ATTRIBUTE_VALIDATORS[key](item_id, value)
コード例 #5
0
ファイル: groups.py プロジェクト: ntrrgc/blockwart
    def validate_name(cls, bundle, name):
        for char in name:
            if char not in _USERNAME_VALID_CHARACTERS:
                raise BundleError(_(
                    "Invalid character in group name '{name}': {char} (bundle '{bundle}')"
                ).format(
                    char=char,
                    bundle=bundle.name,
                    name=name,
                ))

        if name.endswith("_") or name.endswith("-"):
            raise BundleError(_(
                "Group name '{name}' must not end in dash or underscore (bundle '{bundle}')"
            ).format(
                bundle=bundle.name,
                name=name,
            ))

        if len(name) > 30:
            raise BundleError(_(
                "Group name '{name}' is longer than 30 characters (bundle '{bundle}')"
            ).format(
                bundle=bundle.name,
                name=name,
            ))
コード例 #6
0
ファイル: pkg_apt.py プロジェクト: ntrrgc/blockwart
 def ask(self, status):
     before = _("installed") if status.info['installed'] \
         else _("not installed")
     after = green(_("installed")) if self.attributes['installed'] \
         else red(_("not installed"))
     return "{} {} → {}\n".format(
         bold(_("status")),
         before,
         after,
     )
コード例 #7
0
ファイル: symlinks.py プロジェクト: ntrrgc/blockwart
 def validate_name(cls, bundle, name):
     if normpath(name) == "/":
         raise BundleError(_("'/' cannot be a file"))
     if normpath(name) != name:
         raise BundleError(_(
             "'{path}' is an invalid symlink path, should be '{normpath}' (bundle '{bundle}')"
         ).format(
             path=name,
             normpath=normpath(name),
             bundle=bundle.name,
         ))
コード例 #8
0
ファイル: groups.py プロジェクト: ntrrgc/blockwart
 def ask(self, status):
     if not status.info['exists']:
         return _("'{}' not found in /etc/group").format(self.name)
     elif self.attributes['delete']:
         return _("'{}' found in /etc/group. Will be deleted.").format(self.name)
     else:
         return "{} {} → {}\n".format(
             bold(_("GID")),
             status.info['gid'],
             self.attributes['gid'],
         )
コード例 #9
0
    def fix(self, status):
        if not status.info['exists']:
            LOG.info(_("{}:{}: creating...").format(self.node.name, self.id))
        else:
            LOG.info(_("{}:{}: updating...").format(self.node.name, self.id))

        self.node.run("echo '{} {} {}' | debconf-set-selections".format(
            self.attributes['pkg_name'],
            self.name,
            self.attributes['value'],
        ))
        sleep(1)
コード例 #10
0
    def fix(self, status):
        if not status.info['exists']:
            LOG.info(_("{}:{}: creating...").format(self.node.name, self.id))
        else:
            LOG.info(_("{}:{}: updating...").format(self.node.name, self.id))

        self.node.run("echo '{} {} {}' | debconf-set-selections".format(
            self.attributes['pkg_name'],
            self.name,  
            self.attributes['value'],
        ))
        sleep(1)
コード例 #11
0
ファイル: svc_upstart.py プロジェクト: ntrrgc/blockwart
 def fix(self, status):
     if self.attributes['running'] is False:
         LOG.info(_("{node}:{item}: stopping...").format(
             item=self.id,
             node=self.node.name,
         ))
         svc_stop(self.node, self.name)
     else:
         LOG.info(_("{node}:{item}: starting...").format(
             item=self.id,
             node=self.node.name,
         ))
         svc_start(self.node, self.name)
コード例 #12
0
ファイル: groups.py プロジェクト: ntrrgc/blockwart
    def validate_attributes(cls, bundle, item_id, attributes):
        if attributes.get('delete', False):
            for attr in attributes.keys():
                if attr not in ['delete'] + BUILTIN_ITEM_ATTRIBUTES.keys():
                    raise BundleError(_(
                        "{item} from bundle '{bundle}' cannot have other "
                        "attributes besides 'delete'"
                    ).format(item=item_id, bundle=bundle.name))

        if not attributes.get('delete', False) and 'gid' not in attributes.keys():
            raise BundleError(_(
                "{item} from bundle '{bundle}' must define 'gid'"
            ).format(item=item_id, bundle=bundle.name))
コード例 #13
0
ファイル: pkg_apt.py プロジェクト: ntrrgc/blockwart
 def fix(self, status):
     if self.attributes['installed'] is False:
         LOG.info(_("{node}:{item}: removing...").format(
             item=self.id,
             node=self.node.name,
         ))
         pkg_remove(self.node, self.name)
     else:
         LOG.info(_("{node}:{item}: installing...").format(
             item=self.id,
             node=self.node.name,
         ))
         pkg_install(self.node, self.name)
コード例 #14
0
ファイル: users.py プロジェクト: ntrrgc/blockwart
    def ask(self, status):
        if not status.info['exists']:
            return _("'{}' not found in /etc/passwd").format(self.name)
        elif self.attributes['delete']:
            return _("'{}' found in /etc/passwd. Will be deleted.").format(self.name)

        output = ""
        for key, should_value in self.attributes.iteritems():
            if key in ('delete', 'groups', 'hash_method', 'password', 'password_hash',
                       'salt', 'use_shadow'):
                continue
            is_value = status.info[key]
            if should_value != is_value:
                output += "{} {} → {}\n".format(
                    bold(_ATTRIBUTE_NAMES[key]),
                    is_value,
                    should_value,
                )

        if self.attributes['use_shadow']:
            filename = "/etc/shadow"
            found_hash = status.info['shadow_hash']
        else:
            filename = "/etc/passwd"
            found_hash = status.info['passwd_hash']

        if found_hash is None:
            output += bold(_ATTRIBUTE_NAMES['password_hash']) + " " + \
                      _("not found in {}").format(filename) + "\n"
        elif found_hash != self.attributes['password_hash']:
            output += bold(_ATTRIBUTE_NAMES['password_hash']) + " " + \
                      found_hash + "\n"
            output += " " * (len(_ATTRIBUTE_NAMES['password_hash']) - 1) + "→ " + \
                      self.attributes['password_hash'] + "\n"

        groups_should = set(self.attributes['groups'])
        groups_is = set(status.info['groups'])
        missing_groups = list(groups_should.difference(groups_is))
        missing_groups.sort()
        extra_groups = list(groups_is.difference(groups_should))
        extra_groups.sort()

        if missing_groups:
            output += bold(_("missing groups")) + " " + \
                      ", ".join(missing_groups) + "\n"

        if extra_groups:
            output += bold(_("extra groups")) + " " + \
                      ", ".join(extra_groups) + "\n"

        return output
コード例 #15
0
ファイル: directories.py プロジェクト: ntrrgc/blockwart
def validator_mode(item_id, value):
    if not value.isdigit():
        raise BundleError(
            _("mode for {item} should be written as digits, got: '{value}'"
              "").format(item=item_id, value=value)
        )
    for digit in value:
        if int(digit) > 7 or int(digit) < 0:
            raise BundleError(_(
                "invalid mode for {item}: '{value}'"
            ).format(item=item_id, value=value))
    if not len(value) == 3 and not len(value) == 4:
        raise BundleError(_(
            "mode for {item} should be three or four digits long, was: '{value}'"
        ).format(item=item_id, value=value))
コード例 #16
0
ファイル: directories.py プロジェクト: ntrrgc/blockwart
 def get_auto_deps(self, items):
     deps = []
     for item in items:
         if item == self:
             continue
         if (
             (
                 item.ITEM_TYPE_NAME == "file" and
                 is_subdirectory(item.name, self.name)
             )
             or
             (
                 item.ITEM_TYPE_NAME in ("file", "symlink") and
                 item.name == self.name
             )
         ):
             raise BundleError(_(
                 "{item1} (from bundle '{bundle1}') blocking path to "
                 "{item2} (from bundle '{bundle2}')"
             ).format(
                 item1=item.id,
                 bundle1=item.bundle.name,
                 item2=self.id,
                 bundle2=self.bundle.name,
             ))
         elif item.ITEM_TYPE_NAME in ("directory", "symlink"):
             if is_subdirectory(item.name, self.name):
                 deps.append(item.id)
     return deps
コード例 #17
0
ファイル: groups.py プロジェクト: ntrrgc/blockwart
 def fix(self, status):
     if not status.info['exists']:
         LOG.info(_("{node}:{item}: creating...").format(node=self.node.name, item=self.id))
         self.node.run("groupadd -g {gid} {groupname}".format(
             gid=self.attributes['gid'],
             groupname=self.name,
         ))
     elif self.attributes['delete']:
         LOG.info(_("{node}:{item}: deleting...").format(node=self.node.name, item=self.id))
         self.node.run("groupdel {}".format(self.name))
     else:
         LOG.info(_("{node}:{item}: updating...").format(node=self.node.name, item=self.id))
         self.node.run("groupmod -g {gid} {groupname}".format(
             gid=self.attributes['gid'],
             groupname=self.name,
         ))
コード例 #18
0
ファイル: pkg_apt.py プロジェクト: ntrrgc/blockwart
 def validate_attributes(cls, bundle, item_id, attributes):
     if not isinstance(attributes.get('installed', True), bool):
         raise BundleError(_(
             "expected boolean for 'installed' on {item} in bundle '{bundle}'"
         ).format(
             bundle=bundle.name,
             item=item_id,
         ))
コード例 #19
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
def validator_content(item_id, value):
    if value is not None:
        try:
            value.decode('utf-8')
        except UnicodeDecodeError:
            raise BundleError(
                _("'content' for {} must be a UTF-8 encoded string").format(item_id)
            )
コード例 #20
0
ファイル: __init__.py プロジェクト: ntrrgc/blockwart
 def _validate_name(cls, bundle, name):
     if ":" in name:
         raise BundleError(_(
             "invalid name for {type} in bundle '{bundle}': {name} (must not contain colon)"
         ).format(
             bundle=bundle.name,
             name=name,
             type=cls.ITEM_TYPE_NAME,
         ))
コード例 #21
0
    def ask(self, status):
        if not status.info['exists']:
            return _("'{}' not found in debconf-selections").format(self.name)

        return "{}: '{}' > '{}'\n".format(
            self.attributes['pkg_name'],
            status.info['value'],
            self.attributes['value'],
        )
コード例 #22
0
    def ask(self, status):
        if not status.info['exists']:
            return _("'{}' not found in debconf-selections").format(self.name)

        return "{}: '{}' > '{}'\n".format(
            self.attributes['pkg_name'],
            status.info['value'],
            self.attributes['value'],
        )
コード例 #23
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
def diff(content_old, content_new, filename, encoding_hint=None):
    output = ""
    LOG.debug("diffing {filename}: {len_before} B before, {len_after} B after".format(
        filename=filename,
        len_before=len(content_old),
        len_after=len(content_new),
    ))
    start = datetime.now()
    for line in unified_diff(
        content_old.splitlines(True),
        content_new.splitlines(True),
        fromfile=filename,
        tofile=_("<blockwart content>"),
    ):
        suffix = ""
        try:
            line = line.decode('UTF-8')
        except UnicodeDecodeError:
            if encoding_hint and encoding_hint.lower() != "utf-8":
                try:
                    line = line.decode(encoding_hint)
                    suffix += _(" (line encoded in {})").format(encoding_hint)
                except UnicodeDecodeError:
                    line = line[0]
                    suffix += _(" (line not encoded in UTF-8 or {})").format(encoding_hint)
            else:
                line = line[0]
                suffix += _(" (line not encoded in UTF-8)")

        line = line.rstrip("\n")
        if len(line) > DIFF_MAX_LINE_LENGTH:
            line = line[:DIFF_MAX_LINE_LENGTH]
            suffix += _(" (line truncated after {} characters)").format(DIFF_MAX_LINE_LENGTH)
        if line.startswith("+"):
            line = green(line)
        elif line.startswith("-"):
            line = red(line)
        output += line + suffix + "\n"
    duration = datetime.now() - start
    LOG.debug("diffing {file}: complete after {time}s".format(
        file=filename,
        time=duration.total_seconds(),
    ))
    return output
コード例 #24
0
ファイル: actions.py プロジェクト: ntrrgc/blockwart
    def run(self, interactive=False):
        result = self.bundle.node.run(
            self.attributes['command'],
            may_fail=True,
        )

        if self.attributes['expected_return_code'] is not None and \
                not result.return_code == self.attributes['expected_return_code']:
            if not interactive:
                LOG.error("{}:{}: {}".format(
                    self.bundle.node.name,
                    self.id,
                    red(_("FAILED")),
                ))
            raise ActionFailure(_(
                "wrong return code for action '{action}' in bundle '{bundle}': "
                "expected {ecode}, but was {rcode}"
            ).format(
                action=self.name,
                bundle=self.bundle.name,
                ecode=self.attributes['expected_return_code'],
                rcode=result.return_code,
            ))

        if self.attributes['expected_stderr'] is not None and \
                result.stderr != self.attributes['expected_stderr']:
            LOG.error("{}:{}: {}".format(
                self.bundle.node.name,
                self.id,
                red(_("FAILED")),
            ))
            raise ActionFailure(_(
                "wrong stderr for action '{action}' in bundle '{bundle}'"
            ).format(
                action=self.name,
                bundle=self.bundle.name,
            ))

        if self.attributes['expected_stdout'] is not None and \
                result.stdout != self.attributes['expected_stdout']:
            LOG.error("{}:{}: {}".format(
                self.bundle.node.name,
                self.id,
                red(_("FAILED")),
            ))
            raise ActionFailure(_(
                "wrong stdout for action '{action}' in bundle '{bundle}'"
            ).format(
                action=self.name,
                bundle=self.bundle.name,
            ))

        LOG.info("{}:{}: {}".format(
            self.bundle.node.name,
            self.id,
            green(_("OK")),
        ))

        return result
コード例 #25
0
ファイル: directories.py プロジェクト: ntrrgc/blockwart
 def validate_name(cls, bundle, name):
     if normpath(name) != name:
         raise BundleError(_(
             "'{path}' is an invalid directory path, "
             "should be '{normpath}' (bundle '{bundle}')"
         ).format(
             bundle=bundle.name,
             normpath=normpath(name),
             path=name,
         ))
コード例 #26
0
ファイル: __init__.py プロジェクト: ntrrgc/blockwart
def unpickle_item_class(class_name, bundle, name, attributes, has_been_triggered):
    for item_class in bundle.node.repo.item_classes:
        if item_class.__name__ == class_name:
            return item_class(
                bundle,
                name,
                attributes,
                has_been_triggered=has_been_triggered,
                skip_validation=True,
            )
    raise RuntimeError(_("unable to unpickle {cls}").format(cls=class_name))
コード例 #27
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
 def test(self):
     if self.attributes['source'] and not exists(self.template):
         raise BundleError(_(
             "{item} from bundle '{bundle}' refers to missing "
             "file '{path}' in its 'source' attribute"
         ).format(
             bundle=self.bundle.name,
             item=self.id,
             path=self.template,
         ))
     if self.attributes['content_type'] in ('mako', 'text'):
         self.content
コード例 #28
0
ファイル: __init__.py プロジェクト: ntrrgc/blockwart
 def _check_bundle_collisions(self, items):
     for item in items:
         if item == self:
             continue
         if item.id == self.id:
             raise BundleError(_(
                 "duplicate definition of {item} in bundles '{bundle1}' and '{bundle2}'"
             ).format(
                 item=item.id,
                 bundle1=item.bundle.name,
                 bundle2=self.bundle.name,
             ))
コード例 #29
0
ファイル: actions.py プロジェクト: ntrrgc/blockwart
    def get_result(self, interactive=False, interactive_default=True):
        if interactive is False and self.attributes['interactive'] is True:
            return self.STATUS_ACTION_SKIPPED

        if self.triggered and not self.has_been_triggered:
            LOG.debug(_("skipping {} because it wasn't triggered").format(self.id))
            return self.STATUS_ACTION_SKIPPED

        if self.unless:
            unless_result = self.bundle.node.run(
                self.unless,
                may_fail=True,
            )
            if unless_result.return_code == 0:
                LOG.debug(_("{node}:action:{name}: failed 'unless', not running").format(
                    name=self.name,
                    node=self.bundle.node.name,
                ))
                return self.STATUS_ACTION_SKIPPED

        if (
            interactive and
            self.attributes['interactive'] is not False
            and not ask_interactively(
                wrap_question(
                    self.id,
                    self.attributes['command'],
                    _("Run action {}?").format(
                        bold(self.name),
                    ),
                ),
                interactive_default,
            )
        ):
            return self.STATUS_ACTION_SKIPPED
        try:
            self.run(interactive=interactive)
            return self.STATUS_ACTION_OK
        except ActionFailure:
            return self.STATUS_ACTION_FAILED
コード例 #30
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
 def fix(self, status):
     for fix_type in ('type', 'content', 'mode', 'owner', 'group'):
         if fix_type in status.info['needs_fixing']:
             if fix_type == 'group' and \
                     'owner' in status.info['needs_fixing']:
                 # owner and group are fixed with a single chown
                 continue
             if fix_type in ('mode', 'owner', 'group') and \
                     'content' in status.info['needs_fixing']:
                 # fixing content implies settings mode and owner/group
                 continue
             if status.info['path_info'].exists:
                 if self.attributes['delete']:
                     LOG.info(_("{node}:{item}: deleting...").format(
                         node=self.node.name, item=self.id))
                 else:
                     LOG.info(_("{node}:{item}: fixing {type}...").format(
                         node=self.node.name, item=self.id, type=fix_type))
             else:
                 LOG.info(_("{node}:{item}: creating...").format(
                     node=self.node.name, item=self.id))
             getattr(self, "_fix_" + fix_type)(status)
コード例 #31
0
ファイル: __init__.py プロジェクト: ntrrgc/blockwart
 def _validate_required_attributes(cls, bundle, item_id, attributes):
     missing = []
     for attrname in cls.REQUIRED_ATTRIBUTES:
         if attrname not in attributes:
             missing.append(attrname)
     if missing:
         raise BundleError(_(
             "{item} in bundle '{bundle}' missing required attribute(s): {attrs}"
         ).format(
             item=item_id,
             bundle=bundle.name,
             attrs=", ".join(missing),
         ))
コード例 #32
0
ファイル: files.py プロジェクト: ntrrgc/blockwart
def content_processor_mako(item):
    from mako.template import Template
    template = Template(
        item._template_content,
        input_encoding='utf-8',
        output_encoding=item.attributes['encoding'],
    )
    LOG.debug("{}:{}: rendering with Mako...".format(item.node.name, item.id))
    start = datetime.now()
    try:
        content = template.render(
            item=item,
            bundle=item.bundle,
            node=item.node,
            repo=item.node.repo,
            **item.attributes['context']
        )
    except Exception as e:
        LOG.debug("".join(format_exception(*exc_info())))
        if isinstance(e, NameError) and e.message == "Undefined":
            # Mako isn't very verbose here. Try to give a more useful
            # error message - even though we can't pinpoint the excat
            # location of the error. :/
            e = _("Undefined variable (look for '${...}')")
        raise TemplateError(_(
            "Error while rendering template for {node}:{item}: {error}"
        ).format(
            error=e,
            item=item.id,
            node=item.node.name,
        ))
    duration = datetime.now() - start
    LOG.debug("{node}:{item}: rendered in {time}s".format(
        item=item.id,
        node=item.node.name,
        time=duration.total_seconds(),
    ))
    return content