def resolve_base(self, options): """resolve a base object from a set of '--typeid value' options.""" connection = self.check_connection() path = {} for opt in options: if not opt.endswith('id'): continue typename = opt[2:-2] info = schema.type_info(typename) if info is None: self.error('unknown type: %s' % typename) path[info[3]] = (options[opt], info) base = connection.get(schema.API) info = schema.type_info(type(base)) while path: links = connection.get_links(base) for link in links: if link in path: break else: self.error('cannot find object in %s' % info[2]) id, info = path.pop(link) base = self.get_object(info[0], id, base) if base is None: self.error('%s does not exist: %s' % (info[2], id)) return base
def resolve_type(self, name): """return a rhev.schema.* mapping type for a type name.""" info = schema.type_info(name) if info is None: plural = schema.plural(name) if plural is None: self.error('no such type: %s' % name) info = schema.type_info(name) assert info is not None return info[0]._SupersedingClass() return info[1]._SupersedingClass()
def show_help(self): """Show help for "update".""" args = self.arguments opts = self.options connection = self.check_connection() subst = {} if len(args) < 2: helptext = self.helptext0 types = self.get_singular_types() subst['types'] = self.format_list(types) elif len(args) == 2: info = schema.type_info(args[0]) if info is None: self.error('no such type: %s' % args[0]) base = self.resolve_base(opts) obj = self.get_object(info[0], args[1], base=base) if obj is None: self.error('no such %s: %s' % (args[0], args[1])) methods = connection.get_methods(obj) if 'PUT' not in methods: self.error('type cannot be updated: %s' % args[0]) helptext = self.helptext1 subst['type'] = args[0] options = self.get_options(info[0], 'U') subst['options'] = self.format_list(options) statuses = self.get_statuses() subst['statuses'] = self.format_list(statuses) helptext = self.format_help(helptext, subst) stdout = self.context.terminal.stdout stdout.write(helptext)
def show_help(self): """Show help for "create".""" args = self.arguments opts = self.options connection = self.check_connection() stdout = self.context.terminal.stdout subst = {} if len(args) == 0: helptext = self.helptext0 types = self.get_singular_types() subst['types'] = self.format_list(types) elif len(args) == 1: info = schema.type_info(args[0]) if info is None: self.error('unknown type: %s' % args[0]) base = self.resolve_base(opts) methods = connection.get_methods(info[1], base=base) if 'POST' not in methods: self.error('type cannot be created: %s' % args[0]) helptext = self.helptext1 subst['type'] = args[0] options = self.get_options(info[0], 'C') subst['options'] = self.format_list(options) statuses = self.get_statuses() subst['statuses'] = self.format_list(statuses) helptext = self.format_help(helptext, subst) stdout.write(helptext)
def _resolve_url(self, typ, base=None, id=None, search=None, special=None, **query): """INTERNAL: resolve a relationship `name' under the URL `base'.""" info = schema.type_info(typ) if info is None: raise TypeError, 'Unknown binding type: %s' % typ rel = info[3] if rel is None: raise TypeError, 'Can\'t resolve URLs for type: %s' % typ elif rel.startswith('/'): return self._join_path(self.entrypoint, rel) if base is None: base = self.api() if search: special = 'search' elif query: special = 'search' search = ' AND '.join([ '%s=%s' % (k, query[k]) for k in query ]) if special: rel = '%s/%s' % (rel, special) for link in base.link: if link.rel == rel: url = link.href break else: reason = 'relationship %s not found under %s' % (rel, base.href) raise Error(reason) if id is not None: url = self._join_path(url, str(id)) elif search is not None: url = url.replace('{query}', urlencode(search)) return url
def get_attributes(self, typ, prefix=''): """Return a list of valid attributes for a type.""" attrs = [] for elem in typ._ElementMap: name = elem.localName() if name in ('actions', 'link', 'fault'): continue prop = getattr(typ, name) if not isinstance(prop, property): continue subtype = schema.subtype(prop) if issubclass(subtype, schema.ComplexType): info = schema.type_info(subtype) if info is None: attrs += self.get_attributes(subtype, prefix + name + '.') elif info[3]: attrs.append('%s%s.id' % (prefix, name)) attrs.append('%s%s.name' % (prefix, name)) elif issubclass(subtype, schema.SimpleType): attrs.append('%s%s' % (prefix, name)) for attr in typ._AttributeMap: if not prefix and attr in ('id', 'href'): continue name = attr.localName() attrs.append('%s%s' % (prefix, name)) return attrs
def _format_collection(self, collection): context = self.context settings = context.settings stdout = context.terminal.stdout info = schema.type_info(type(collection)) rel = info[2] fields = self._get_fields(rel) fields = self._filter_fields(fields, info[0]) # Calculate the width of each column if stdout.isatty() and not settings['wide']: widths = [ len(f) for f in fields ] for resource in collection: for i in range(len(fields)): value = self._get_value(resource, fields[i]) widths[i] = max(widths[i], len(value)) total = sum(widths) + 2*len(fields) # Now downsize if it doesn't fit if total > context.terminal.width: fraction = 1.0 * context.terminal.width / total fwidths = [0] * len(fields) # Pass 1: round down to nearest integer for i in range(len(fields)): fwidths[i] = widths[i] * fraction widths[i] = int(fwidths[i]) # Pass 2: allocate fractional leftovers to columns available = context.terminal.width - 2*len(fields) - sum(widths) remainders = [ (fwidths[i] - widths[i], i) for i in range(len(fields)) ] remainders.sort(reverse=True) for i in range(min(len(fields), available)): widths[remainders[i][1]] += 1 formats = ['%%-%d.%ds' % (w, w) for w in widths ] else: widths = [ sys.maxint ] * len(fields) formats = [ '%s' ] * len(fields) if settings['header']: # Header for i in range(len(fields)): stdout.write(formats[i] % fields[i]) if i != len(fields)-1: stdout.write(' ') stdout.write('\n') # Horizontal line for i in range(len(fields)): stdout.write('-' * widths[i]) if i != len(fields)-1: stdout.write(' ') stdout.write('\n') # Data elements for resource in collection: values = [ self._get_value(resource, field) for field in fields ] while sum([len(v) for v in values]) > 0: for i in range(len(fields)): stdout.write(formats[i] % values[i]) values[i] = values[i][widths[i]:] if i != len(fields)-1: stdout.write(' ') stdout.write('\n') stdout.write('\n')
def resolve_singular_type(self, name): """return a singular type only.""" plural = schema.plural(name) if plural is None: self.error('no such type: %s' % name) info = schema.type_info(plural) assert info is not None return info[0]._SupersedingClass()
def show_help(self): """Show help for the action command.""" args = self.arguments opts = self.options connection = self.check_connection() stdout = self.context.terminal.stdout subst = {} if len(args) < 2: helptext = self.helptext0 types = self.get_singular_types() subst['types'] = self.format_list(types) elif len(args) == 2: helptext = self.helptext1 info = schema.type_info(args[0]) if info is None: self.error('no such type: %s' % args[0]) subst['type'] = args[0] subst['id'] = args[1] base = self.resolve_base(opts) obj = self.get_object(info[0], args[1], base) if obj is None: self.error('no such %s: %s' % (args[0], args[1])) actions = connection.get_actions(obj) subst['actions'] = self.format_list(actions) elif len(args) == 3: helptext = self.helptext1 info = schema.type_info(args[0]) if info is None: self.error('no such type: %s' % args[0]) subst['type'] = args[0] subst['id'] = args[1] subst['action'] = args[2] base = self.resolve_base(self.options) obj = self.get_object(info[0], args[1], base) if obj is None: self.error('no such %s: %s' % (args[0], args[1])) actions = connection.get_actions(obj) if args[2] not in actions: self.error('no such action: %s' % args[2]) scope = '%s:%s' % (info[0].__name__, args[2]) options = self.get_options(schema.Action, 'C', scope=scope) subst['options'] = self.format_list(options, bullet='') statuses = self.get_statuses() subst['statuses'] = self.format_list(statuses) helptext = self.format_help(helptext, subst) stdout.write(helptext)
def _get_types(self, plural): """INTERNAL: return a list of types.""" connection = self.check_connection() links = connection.get_links(connection.api()) types = [ schema.type_info(link) for link in links ] ix = 2 + int(plural) types = [ info[ix] for info in types if info and info[ix] ] return types
def _format_collection(self, collection, scope=None): context = self.context settings = context.settings stdout = context.terminal.stdout info = schema.type_info(type(collection)) fields = self._get_fields(info[0], "L", scope) # Calculate the width of each column if stdout.isatty() and not settings["wide"]: widths = [len(f.name) for f in fields] for resource in collection: for i in range(len(fields)): value = fields[i].get(resource, self.context) widths[i] = max(widths[i], len(value)) total = sum(widths) + 2 * len(fields) # Now downsize if it doesn't fit if total > context.terminal.width: fraction = 1.0 * context.terminal.width / total fwidths = [0] * len(fields) # Pass 1: round down to nearest integer for i in range(len(fields)): fwidths[i] = widths[i] * fraction widths[i] = int(fwidths[i]) # Pass 2: allocate fractional leftovers to columns available = context.terminal.width - 2 * len(fields) - sum(widths) remainders = [(fwidths[i] - widths[i], i) for i in range(len(fields))] remainders.sort(reverse=True) for i in range(min(len(fields), available)): widths[remainders[i][1]] += 1 formats = ["%%-%d.%ds" % (w, w) for w in widths] else: widths = [sys.maxint] * len(fields) formats = ["%s"] * len(fields) if settings["header"]: # Header for i in range(len(fields)): stdout.write(formats[i] % fields[i].name) if i != len(fields) - 1: stdout.write(" ") stdout.write("\n") # Horizontal line for i in range(len(fields)): stdout.write("-" * widths[i]) if i != len(fields) - 1: stdout.write(" ") stdout.write("\n") # Data elements for resource in collection: values = [field.get(resource, self.context) for field in fields] while sum([len(v) for v in values]) > 0: for i in range(len(fields)): stdout.write(formats[i] % values[i]) values[i] = values[i][widths[i] :] if i != len(fields) - 1: stdout.write(" ") stdout.write("\n") stdout.write("\n")
def execute(self): """Execute "list".""" args = self.arguments opts = self.options connection = self.check_connection() info = schema.type_info(args[0]) if info is None: self.error("no such type: %s" % args[0]) base = self.resolve_base(opts) search = " ".join(self.arguments[1:]) result = connection.getall(info[0], base=base, search=search) self.context.formatter.format(self.context, result)
def _get_fields(self, typ, flag, scope=None): info = schema.type_info(typ) assert info is not None override = self.context.settings.get("fields.%s" % info[2]) if override is None: override = self.context.settings.get("fields") if override is None: fields = metadata.get_fields(typ, flag, scope) else: override = override.split(",") fields = metadata.get_fields(typ, "") fields = filter(lambda f: f.name in override, fields) return fields
def _fixup_object(self, obj): """INTERNAL: fixup a mapping object.""" # Here we put stuff that we'd like to become part of the API itself. if isinstance(obj, schema.API): obj.href = self.entrypoint if hasattr(obj, 'link'): for link in obj.link: info = schema.type_info(link.rel, base=obj) if info: link.type = info[0].__name__ if hasattr(obj, 'actions') and hasattr(obj.actions, 'link'): for link in obj.actions.link: link.type = 'Action' return obj
def _resolve_url(self, typ, base=None, id=None, search=None, special=None, **query): """INTERNAL: resolve a relationship `name' under the URL `base'.""" info = schema.type_info(typ) if info is None: raise TypeError, 'Unknown binding type: %s' % typ rel = info[2] if rel is None: raise TypeError, 'Can\'t resolve URLs for type: %s' % typ elif rel.startswith('/'): return self._join_path(self.entrypoint, rel) if base is not None: if hasattr(base, 'href'): base = base.href else: base = self._resolve_url(type(base)) else: base = self.entrypoint if search: special = 'search' elif query: special = 'search' search = ' AND '.join([ '%s=%s' % (k, query[k]) for k in query ]) if special: rel = '%s/%s' % (rel, special) if base not in self._cache: response = self._make_request('GET', base) if response.status != http.OK: raise self._create_exception(response) parsed = self._parse_xml(response) self._cache[base] = parsed else: parsed = self._cache[base] for link in parsed.link: if link.rel == rel: url = link.href break else: reason = 'relationship %s not found under %s' % (rel, base) raise Error(reason) if id is not None: url = self._join_path(url, str(id)) elif search is not None: url = url.replace('{query}', urlencode(search)) return url
def execute(self): """Execute "delete".""" args = self.arguments opts = self.options connection = self.check_connection() base = self.resolve_base(opts) info = schema.type_info(args[0], base=base) if info is None: self.error('no such type: %s' % args[0]) for link in base.link: if link.rel == info[3]: break else: self.error('type does not exist here: %s' % args[0]) obj = self.get_object(info[0], args[1], base) if obj is None: self.error('object does not exist') result = connection.delete(obj) self.context.formatter.format(self.context, result)
def execute(self): """Execute the "create" command.""" args = self.arguments opts = self.options connection = self.check_connection() stdout = self.context.terminal.stdout base = self.resolve_base(opts) info = schema.type_info(args[0], base=base) if info is None: self.error('no such type: %s' % args[0]) for link in base.link: if link.rel == info[3]: break else: self.error('type cannot be created here: %s' % args[0]) obj = self.read_object() if obj is None: obj = self.create_object(info[0], opts) connection.create(obj, base=base)
def execute(self): """Execute the "update" command.""" args = self.arguments opts = self.options connection = self.check_connection() base = self.resolve_base(opts) info = schema.type_info(args[0], base=base) if info is None: self.error('no such type: %s' % args[0]) for link in base.link: if link.rel == info[3]: break else: self.error('type does not exist here: %s' % args[0]) obj = self.get_object(info[0], args[1], base) if obj is None: self.error('object does not exist') # Trac issue #179: don't set fields that already exist obj = schema.href(obj) self.update_object(obj, self.options) connection.update(obj)
def get_object(self, typ, id, base=None): """Return an object by id or name.""" self.check_connection() connection = self.context.connection # Try by id try: obj = connection.get(typ, id=id, base=base) except rhev.Error: obj = None # work around RHEV issue #120 if obj is not None: return obj # Try by name: either search, or filter info = schema.type_info(typ) if base is None: base = connection.get(schema.API) links = connection.get_links(base) rel = '%s/search' % info[3] if rel in links: obj = connection.get(typ, name=id, base=base) else: obj = connection.get(typ, base=base, filter={'name': id}) return obj
def execute(self): """Execute the action command.""" args = self.arguments opts = self.options connection = self.check_connection() base = self.resolve_base(opts) info = schema.type_info(args[0], base=base) if info is None: self.error('no such type: %s' % args[0]) for link in base.link: if link.rel == info[3]: break else: self.error('type does not exist here: %s' % args[0]) obj = self.get_object(info[0], args[1], base) if obj is None: self.error('object does not exist: %s/%s', (args[0], args[1])) scope = '%s:%s' % (info[0].__name__, args[2]) action = self.create_object(schema.Action, opts, scope=scope) try: result = connection.action(obj, args[2], action) except rhev.Error, e: self.error(str(e))
def resolve_plural_type(self, name): """resolve a plural type only.""" info = schema.type_info(name) if info is None: self.error('no such type: %s' % name) return info[1]._SupersedingClass()