class CreateObjCmd(Cmd):
    implements(ICmdArgumentsSyntax)

    command('mk')
    alias('create')

    @db.ro_transact(proxy=False)
    def subject(self, args):
        return tuple((self.current_obj, ))

    @db.transact
    def arguments(self):
        parser = VirtualConsoleArgumentParser()

        obj = self.current_obj

        choices = []
        if getattr(obj, '__contains__', None):
            for name, cls in creatable_models.items():
                if obj.can_contain(cls):
                    choices.append(name)

        parser.add_argument('type',
                            choices=choices,
                            help="object type to be created")
        return parser

    @db.transact
    def execute(self, args):
        model_cls = creatable_models.get(args.type)

        form = RawDataValidatingFactory(args.keywords,
                                        model_cls,
                                        marker=getattr(self.current_obj,
                                                       '__contains__', None))

        if form.errors:
            form.write_errors(to=self)
            return

        obj = form.create()

        vh = PreValidateHookMixin(obj)
        try:
            blocking_yield(vh.validate_hook(self.protocol.principal))
        except Exception:
            msg = 'Cancelled executing "%s" due to validate_hook failure' % self.name
            self.write('%s\n' % msg)
            log.msg(msg, system='set')
            return

        interaction = self.protocol.interaction
        if not interaction:
            auth = getUtility(IAuthentication, context=None)
            principal = auth.getPrincipal(None)
        else:
            principal = interaction.participations[0].principal

        obj.__owner__ = principal

        obj_id = self.current_obj.add(obj)

        self.write("%s\n" % obj_id)
class CatObjectCmd(Cmd):
    implements(ICmdArgumentsSyntax)

    command('cat')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('paths', nargs='+')
        parser.add_argument('-o', action='append')
        parser.add_argument('-H', action='store_true')
        parser.add_argument(
            '-l',
            action='store_true',
            default=False,
            help='Show each item of a collection property on its own line')
        return parser

    @db.ro_transact(proxy=False)
    def subject(self, args):
        return tuple(self.traverse(path) for path in args.paths)

    @db.ro_transact
    def execute(self, args):
        attrs = []
        for i in args.o or []:
            for attr in i.split(','):
                attrs.append(attr.strip())

        for path in args.paths:
            obj = self.traverse(path)
            if not obj:
                self.write("No such object: %s\n" % path)
            else:
                self._do_cat(obj, attrs, args.l, path if args.H else None)

    def _do_cat(self, obj, attrs, multiline, filename=None):
        name = '%s: ' % filename if filename else ''

        data = [(key, value, name + title) for (key, value), title in zip(
            model_to_dict(obj).items(),
            model_to_dict(obj, use_titles=True).keys())
                if key in attrs or not attrs]

        log.msg('data: %s' % data, system='cat-cmd')

        if data:
            max_title_len = max(len(title) for key, _, title in data) + 1
            for key, value, title in data:
                if isinstance(value, dict):
                    if multiline:
                        separator = '\n ' + ' ' * max_title_len
                    else:
                        separator = ', '
                    # security proxies don't mimic tuple() perfectly
                    # thus cannot be passed to "%" directly
                    pretty_value = separator.join(
                        ['%s: %s' % tuple(i) for i in value.items()])
                elif hasattr(value, '__iter__'):
                    strings = [str(i) for i in value]
                    if not isinstance(value, tuple):
                        strings = sorted(strings)
                    if multiline:
                        separator = '\n ' + ' ' * max_title_len
                    else:
                        separator = ', '
                    pretty_value = separator.join(strings)
                elif key in ('mtime', 'ctime'):
                    pretty_value = datetime.datetime.fromtimestamp(
                        value).isoformat()
                else:
                    pretty_value = value
                self.write("%s %s\n" %
                           ((title.encode('utf8') + ':').ljust(max_title_len),
                            str(pretty_value).encode('utf8')))

        if not attrs and IIncomplete.providedBy(obj):
            self.write("-----------------\n")
            self.write("This %s is incomplete.\n" %
                       (type(removeSecurityProxy(obj)).__name__))
class SetAttrCmd(Cmd):
    implements(ICmdArgumentsSyntax)

    command('set')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('path')
        return parser

    @db.ro_transact(proxy=False)
    def subject(self, args):
        return tuple((self.traverse(args.path), ))

    @defer.inlineCallbacks
    def execute(self, args):
        obj = (yield db.ro_transact(self.traverse)(args.path))
        if not obj:
            self.write("No such object: %s\n" % args.path)
            return

        vh = PreValidateHookMixin(obj)
        try:
            yield vh.validate_hook(self.protocol.principal)
        except Exception:
            msg = 'Canceled executing "set" due to validate_hook failure'
            self.write('%s\n' % msg)
            log.msg(msg, system='set')
            return

        @db.transact
        def apply():
            obj = self.traverse(args.path)
            raw_data = args.keywords

            if args.verbose:
                for key, value in raw_data.items():
                    self.write("Setting %s=%s\n" % (key, value))

            principals = map(lambda p: p.id, effective_principals(self.user))

            if 'admins' in principals:
                setattr(obj, '_admin_access', True)
            elif getattr(obj, '_admin_access', False):
                log.msg(
                    'WARNING: admin access in SetAttrCmd when accessed by non-admin (%s: %s)!'
                    % (self.user, principals),
                    system='set-cmd')

            try:
                form = RawDataApplier(raw_data, obj)

                if not form.errors:
                    form.apply()
                else:
                    form.write_errors(to=self)
            finally:
                if hasattr(obj, '_admin_access'):
                    delattr(obj, '_admin_access')

        yield apply()
class ListDirContentsCmd(Cmd):
    implements(ICmdArgumentsSyntax)

    command('ls')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('-l', action='store_true', help="long")
        parser.add_argument('-R', action='store_true', help="recursive")
        parser.add_argument(
            '-d',
            action='store_true',
            help=
            "list directory entries instead of contents, and do not dereference "
            "symbolic links")
        parser.add_argument('paths', nargs='*')
        return parser

    @db.ro_transact
    def execute(self, args):
        self.opts_long = args.l
        self.opts_dir = args.d
        self.visited = []

        if args.paths:
            for path in args.paths:
                obj = self.traverse(path)
                if not obj:
                    self.write('No such object: %s\n' % path)
                else:
                    self._do_ls(obj, path, recursive=args.R)
        else:
            self._do_ls(self.current_obj, recursive=args.R)

    @db.ro_transact(proxy=False)
    def subject(self, args):
        if args.paths:
            return tuple(self.traverse(path) for path in args.paths)
        else:
            return tuple((self.current_obj, ))

    def _do_ls(self, obj, path='.', recursive=False):
        assert obj not in self.visited
        self.visited.append(obj)

        def pretty_name(item):
            if IContainer.providedBy(item):
                return self.protocol.colorize(BLUE, '%s/' % (item.__name__, ))
            elif ICommand.providedBy(item):
                return self.protocol.colorize(GREEN, '%s*' % (item.__name__, ))
            elif isinstance(item, Symlink):
                return self.protocol.colorize(CYAN, '%s@' % (item.__name__, ))
            else:
                return item.__name__

        def make_long_lines(container):
            def get_symlink_nicknames(item):
                for method in (lambda item: [canonical_path(item)],
                               lambda item: getattr(follow_symlinks(item),
                                                    'nicknames', [])):
                    try:
                        for n in method(item):
                            yield n
                    except Unauthorized:
                        log.err(system='security')

            def nick(item):
                return (get_symlink_nicknames(item) if isinstance(
                    item, Symlink) else getattr(item, 'nicknames', []))

            def owner(item):
                return item.__owner__ or 'root'

            return [
                (('%s %s %s\t%s\t%s\n' %
                  (pretty_effective_perms(self.protocol.interaction,
                                          follow_symlinks(subobj)),
                   owner(subobj), datetime.datetime.fromtimestamp(
                       subobj.mtime).isoformat() if not subobj.__transient__
                   else '         <transient>         ', pretty_name(subobj),
                   ' : '.join(nick(subobj)))).encode('utf-8'))
                for subobj in container
            ]

        def make_short_lines(container):
            return columnize([pretty_name(subobj) for subobj in container],
                             displaywidth=self.protocol.width)

        def filter_by_permission(i):
            try:
                return self.protocol.interaction.checkPermission('view', i)
            except Exception as e:
                log.msg('Error accessing %s' % i, system='ls')
                log.err(e)

        container = (sorted(filter(filter_by_permission, obj.listcontent()),
                            key=lambda o: o.__name__)
                     if IContainer.providedBy(obj) and not self.opts_dir else
                     [obj])

        for line in (make_long_lines(container)
                     if self.opts_long else make_short_lines(container)):
            self.write(line)

        if recursive and IContainer.providedBy(obj) and not self.opts_dir:
            for ch in container:
                child_obj = obj[ch.__name__]
                if (IContainer.providedBy(child_obj)
                        and not isinstance(child_obj, Symlink)
                        and child_obj not in self.visited):
                    self.write("\n%s:\n" %
                               os.path.join(path, ch.__name__.encode('utf8')))
                    self._do_ls(child_obj,
                                os.path.join(path, ch.__name__),
                                recursive=True)
class PrintWorkDirCmd(Cmd):
    command('pwd')

    def execute(self, args):
        self.write('%s\n' % self.protocol._cwd())
示例#6
0
class ExportMetadataCmd(Cmd, SetAclMixin):
    implements(ICmdArgumentsSyntax)
    command('importexport')

    serialize_action_map = {'json': json.dumps, 'yaml': yaml.dump}
    deserialize_action_map = {'json': json.loads, 'yaml': yaml.load}

    traverse_paths = (('/machines/', True), ('/ippools/', False),
                      ('/templates/', False), ('/home/', False))

    type_blacklist = ('IncomingMachines', 'ByNameContainer',
                      'ActionsContainer')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument(
            'filename',
            help='OS file path where the data to import from or export to')
        parser.add_argument('-i',
                            '--import-data',
                            action='store_true',
                            help='Import data')
        parser.add_argument('-f',
                            '--format',
                            choices=['json', 'yaml'],
                            help='Input/output file format',
                            default='yaml')
        parser.add_argument('-p',
                            '--full-path',
                            action='store_true',
                            help='Add full OMS paths (export-only)',
                            default=False)
        parser.add_argument('-m',
                            '--max-depth',
                            type=int,
                            help='Max path recursion depth',
                            default=5)
        parser.add_argument('-a',
                            '--attributes',
                            type=list,
                            help='List of attributes to import/export',
                            default=[])
        return parser

    @require_admins_only
    @defer.inlineCallbacks
    def execute(self, args):
        log.msg('Exporting all object ownership data...')
        if args.import_data:
            yield self.import_data(args)
        else:
            yield self.export_data(args)

    @db.transact
    def import_data(self, args):
        with open(args.filename, 'r') as f:
            serialized = f.read()
            data = self.deserialize_action_map.get(args.format)(serialized)

        for path, recursive in self.traverse_paths:
            container = traverse1(path)
            pdata = data.get(path)
            if container and pdata:
                self.write(
                    'Importing %s (%s)...\n' %
                    (path, 'recursive' if recursive else 'non-recursive'))
                self.traverse_level_set(pdata,
                                        container,
                                        args.attributes,
                                        recursive=recursive,
                                        maxlevel=args.max_depth)

    @db.assert_transact
    def traverse_level_set(self,
                           data,
                           container,
                           attrs,
                           recursive=False,
                           maxlevel=5,
                           level=0):
        def import_cls(module, name):
            mod = __import__(module)
            for comp in module.split('.')[1:]:
                mod = getattr(mod, comp)
            return getattr(mod, name)

        for name, di in data.iteritems():
            self.write('%s%s\n' % (' ' * level, name))
            element = container[name]

            if di['__classname__'] in self.type_blacklist:
                continue

            obj = import_cls(di['__module__'],
                             di['__classname__']) if not element else element

            if obj.__transient__:
                continue

            cobj = self._do_create_or_set(di,
                                          obj,
                                          attrs=attrs,
                                          marker=getattr(
                                              container, '__contains__', None))

            if cobj is None:
                continue

            if not element:
                container.add(cobj)

            if IContainer.providedBy(cobj) and recursive and level < maxlevel:
                chdata = di.get('children')
                if chdata is not None:
                    self.traverse_level_set(chdata,
                                            cobj,
                                            attrs,
                                            recursive=recursive,
                                            maxlevel=maxlevel,
                                            level=level + 1)

    attr_blacklist = (
        '__module__',
        '__name__',
        '__classname__',
        'children',
        'ctime',
        'features',
        'module',
        'mtime',
        'owner',
        'permissions',
        'tags',
        'type',
    )

    def apply_form(self, form_class, data, obj, action, marker=None):
        DoesNotExist = "<NOVALUE>"

        def format_error_message(errors):
            return ('ERROR: %s while importing data for %s\n' %
                    ([(attr, data.get(attr, DoesNotExist), err)
                      for attr, err in errors], obj))

        form = form_class(data, obj, marker=marker)

        if form.errors and any(
                map(lambda
                    (f, err): isinstance(err, NoSchemaFound), form.errors)):
            self.write('WARNING: import of %s failed: no schema is defined\n' %
                       (obj))
            return

        wrong_type_errors = filter(
            lambda (f, err): isinstance(err, WrongType) and data.get(
                f, DoesNotExist) is None, form.errors)

        fields = dict(
            map(lambda (f, t, i): (f, t), get_schema_fields(obj,
                                                            marker=marker)))

        # Attempt to fix wrong type errors by setting default values of the respective types
        for field_name, err in wrong_type_errors:
            self.write(
                'Attempting to fix field "%s" of %s with a WrontType error...  '
                % (field_name, obj))
            try:
                field = fields[field_name]

                if isinstance(field._type, tuple):
                    default = field._type[0]()
                else:
                    default = field._type()

                data[field_name] = default
            except ValueError:
                self.write('Failed!\n')
            else:
                self.write('Done.\n')

        # List missing required fields
        missing_fields = filter(
            lambda (f, err): isinstance(err, RequiredMissing), form.errors)
        for field_name, err in missing_fields:
            self.write('Missing required field: %s %s in %s' %
                       (obj, field_name, data.keys()))

        # Force renewal of the validation
        delattr(form, '_errors')

        assert not form.errors, format_error_message(form.errors)
        return getattr(form, action)(ignore_readonly=True)
示例#7
0
class MemoryProfileCmd(Cmd):
    implements(ICmdArgumentsSyntax)
    command('memoryprofile')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('-t',
                            action='store_true',
                            help='Force tracking changes')
        parser.add_argument('-s', help='Show details for particular types')
        parser.add_argument('-d',
                            action='store_true',
                            help='Step into debugger')
        parser.add_argument('-b',
                            action='store_true',
                            help='Step into debugger in DB thread')
        return parser

    @require_admins_only
    @defer.inlineCallbacks
    def execute(self, args):

        if args.d:
            import ipdb
            ipdb.set_trace()
            return

        if args.b:

            @db.ro_transact
            def get_db_object(path):
                import ipdb
                ipdb.set_trace()
                return self.traverse(path)

            yield get_db_object('/')
            return

        if args.t:
            mpdaemon = find_daemon_in_proc(MemoryProfilerDaemonProcess)
            oldtrack = mpdaemon.track
            mpdaemon.track = True

        handler = CommandInterfaceWriter(self)
        logger.addHandler(handler)

        def keystrokeReceived(keyID, mod):
            logger.removeHandler(handler)
            if args.t:
                mpdaemon.track = oldtrack
            r = self.protocol._orig_keystrokeReceived(keyID, mod)
            self.protocol.keystrokeReceived = self.protocol._orig_keystrokeReceived
            return r

        self.protocol._orig_keystrokeReceived = self.protocol.keystrokeReceived
        self.protocol.keystrokeReceived = keystrokeReceived

        try:
            while True:
                yield async_sleep(1)
        finally:
            logger.removeHandler(handler)
            if args.t:
                mpdaemon.track = oldtrack

            self.protocol.keystrokeReceived = self.protocol._orig_keystrokeReceived
示例#8
0
class WhoAmICmd(Cmd):
    command('whoami')

    def execute(self, args):
        self.write("%s\n" % self.protocol.principal.id)
示例#9
0
class ChownCmd(Cmd):
    implements(ICmdArgumentsSyntax)
    command('chown')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('user', help='User name')
        parser.add_argument('paths', nargs='+', help='List of paths')
        parser.add_argument('-R',
                            '--recursive',
                            action='store_true',
                            help='Change ownership recursively',
                            default=False,
                            required=False)
        parser.add_argument('-l',
                            '--limit',
                            type=int,
                            default=5,
                            help='Recursion limit (default=5)')
        parser.add_argument('-v',
                            '--verbose',
                            action='store_true',
                            help='Display console warnings about '
                            'transient objects')
        return parser

    @require_admins_only
    @defer.inlineCallbacks
    def execute(self, args):
        auth = getUtility(IAuthentication, context=None)
        principal = auth.getPrincipal(args.user)

        if not principal:
            self.write('No such user: %s\n' % (args.user))
            return

        def set_owner(path, level):
            target = self.traverse(path)

            if not target:
                self.write('Not found: %s\n' % path)
                return

            if target.__transient__:
                if args.verbose:
                    self.write(
                        "Transient object %s cannot have its owner changed\n" %
                        path)
                return

            target.__owner__ = principal

            if IContainer.providedBy(
                    target) and args.recursive and level < args.limit:
                for item in target.listcontent():
                    set_owner(os.path.join(path, item.__name__), level + 1)

        @db.transact
        def set_owner_all():
            for path in args.paths:
                set_owner(path, 0)

        yield set_owner_all()
示例#10
0
class GetAclCmd(Cmd):
    implements(ICmdArgumentsSyntax)

    command('getfacl')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('paths', nargs='+')
        parser.add_argument('-v',
                            action='store_true',
                            help='Show grants for every permission')
        parser.add_argument('-R',
                            '--recursive',
                            action='store_true',
                            help='Print permissions recursively')
        return parser

    @db.ro_transact
    def execute(self, args):
        for path in args.paths:
            obj = self.traverse(path)
            if not obj:
                self.write("No such object %s\n" % path)
                continue

            self._do_print_acl(obj, args.v, args.recursive, [obj])

    def _do_print_acl(self, obj, verbose, recursive, seen):
        prinrole = IPrincipalRoleManager(obj)
        auth = getUtility(IAuthentication, context=None)

        user_allow = collections.defaultdict(list)
        user_deny = collections.defaultdict(list)
        users = set()
        for role, principal, setting in prinrole.getPrincipalsAndRoles():
            users.add(principal)
            if setting.getName() == 'Allow':
                user_allow[principal].append(role)
            else:
                user_deny[principal].append(role)

        for principal in users:

            def formatted_perms(perms):
                prin = auth.getPrincipal(principal)
                typ = 'group' if isinstance(prin, Group) else 'user'
                if verbose:

                    def grants(i):
                        return ','.join(
                            '@%s' % i[0] for i in
                            rolePermissionManager.getPermissionsForRole(i)
                            if i[0] != 'oms.nothing')

                    return (typ, principal, ''.join(
                        '%s{%s}' %
                        (Role.role_to_nick.get(i, '(%s)' % i), grants(i))
                        for i in sorted(perms)))
                else:
                    return (typ, principal, ''.join(
                        Role.role_to_nick.get(i, '(%s)' % i)
                        for i in sorted(perms)))

            if principal in user_allow:
                self.write("%s:%s:+%s\n" %
                           formatted_perms(user_allow[principal]))
            if principal in user_deny:
                self.write("%s:%s:-%s\n" %
                           formatted_perms(user_deny[principal]))

        if recursive and IContainer.providedBy(follow_symlinks(obj)):
            for sobj in follow_symlinks(obj).listcontent():
                if follow_symlinks(sobj) not in seen:
                    seen.append(sobj)
                    self.write('%s:\n' % canonical_path(sobj))
                    self._do_print_acl(sobj, verbose, recursive, seen)
示例#11
0
            class FailingCommand(Cmd):
                command('fail')

                def execute(self, args):
                    raise Exception('some mock error')
示例#12
0
class DBAdminCat(Cmd):
    """ Renders objects stored in ZODB by their OID (optionally also transaction ID),
    in case when POSKeyErrors appear. Helps troubleshoot ZODB storage issues. """
    implements(ICmdArgumentsSyntax)

    command('dbcat')

    def arguments(self):
        parser = VirtualConsoleArgumentParser()
        parser.add_argument('oids', nargs='+', help="List of OIDs to retrieve from DB")
        parser.add_argument('--tid', nargs='?', help="Transaction ID which the objects to retrieve for")
        return parser

    @require_admins_only
    @db.ro_transact
    def execute(self, args):
        for oid in args.oids:
            try:
                obj = db.load_object(oid, args.tid)
            except Exception:
                self.write("No such object: %s\n" % oid)
            else:
                self._do_dbcat(obj, None)

    def _get_data(self, obj):
        for key in dir(obj):
            if key in ('__doc__',
                       '__dict__',
                       '_p_oid',
                       '_p_changed',
                       '_p_estimated_size',
                       '_p_jar',
                       '_p_mtime',
                       '_p_serial',
                       '_p_state',
                       '__markers__',
                      ):
                continue

            attr = getattr(obj, key)

            if callable(attr):
                continue

            if not isinstance(attr, property):
                yield (key, attr)
            else:
                try:
                    yield (key, attr.__get__(obj))
                    continue
                except TypeError as e:
                    pass
                except AttributeError as e:
                    pass
                yield (key, 'Error: "%s"' % e)

    def _do_dbcat(self, obj, filename=None):
        data = list(self._get_data(obj))
        max_title_len = max(len(title) for title, _ in data)

        for title, value in data:
            if isinstance(value, dict):
                # security proxies don't mimic tuple() perfectly
                # thus cannot be passed to "%" directly
                pretty_value = ', '.join(['%s:%s' % tuple(i) for i in value.items()])
            elif hasattr(value, '__iter__'):
                strings = [str(i) for i in value]
                if not isinstance(value, tuple):
                    strings = sorted(strings)
                pretty_value = ', '.join(strings)
            elif title in ('mtime', 'ctime') and isinstance(value, float):
                pretty_value = datetime.datetime.fromtimestamp(value).isoformat()
            else:
                pretty_value = value

            self.write("%s\t%s\n" % ((title.encode('utf8') + ':').ljust(max_title_len),
                                     str(pretty_value).encode('utf8')))

        if IIncomplete.providedBy(obj):
            self.write("-----------------\n")
            self.write("This %s is incomplete.\n" % (type(removeSecurityProxy(obj)).__name__))