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())
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)
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
class WhoAmICmd(Cmd): command('whoami') def execute(self, args): self.write("%s\n" % self.protocol.principal.id)
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()
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)
class FailingCommand(Cmd): command('fail') def execute(self, args): raise Exception('some mock error')
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__))