class Recommendations(Config, ReprMixin): """Recommendations on how to use the Lava API for a given workload""" name = Field(six.text_type, required=True) description = Field(six.text_type, required=True) requires = ListField(six.text_type, required=True) sizes = ListField(Size, required=True, help='See: :class:`Size`') @property def _description(self): return '\n'.join(textwrap.wrap(self.description, 30))
class CreateStackRequest(Config): name = Field(six.text_type, required=True, validator=Length(min=1, max=255)) description = Field(six.text_type, validator=Length(min=1, max=1024)) distro = Field(six.text_type, required=True, validator=Length(min=1, max=255)) services = ListField(Service, required=True) node_groups = ListField(NodeGroup)
class Credentials(Config, ReprMixin): cloud_files = ListField(CloudFilesCredential) ssh_keys = ListField(SSHKey) s3 = ListField(S3Credential) ambari = ListField(AmbariCredential) def display(self): data = chain([('SSH Key', key.name) for key in self.ssh_keys], [('Cloud Files', cred.username) for cred in self.cloud_files], [('Amazon S3', cred.access_key_id) for cred in self.s3], [('Ambari', cred.username) for cred in self.ambari]) print_table(data, ('Type', 'Name'), title='Credentials')
class Script(Config, ReprMixin): table_columns = ('id', 'name', 'type', 'is_public', 'created', 'url') table_header = ('ID', 'Name', 'Type', 'Public', 'Created', 'URL') id = Field(six.text_type, required=True) name = Field(six.text_type, required=True) type = Field(six.text_type, required=True) url = Field(six.text_type, required=True) is_public = Field(bool, required=True) created = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'creation date') updated = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'date last updated') links = ListField(Link) def update(self, **kwargs): """ Update this script. See :meth:`~lavaclient.api.scripts.Resource.update`. """ return self._client.scripts.update(self.id, **kwargs) def delete(self): """ Delete this script. See :meth:`~lavaclient.api.scripts.Resource.delete`. """ return self._client.scripts.delete(self.id)
class ClusterCreateRequest(Config): """POST data to create cluster""" name = Field(six.text_type, required=True, validator=Length(min=1, max=255)) username = Field(six.text_type, required=True, validator=[Length(min=2, max=255), valid_username]) ssh_keys = ListField(six.text_type, required=True, validator=List(Length(min=1, max=255))) stack_id = Field(six.text_type, required=True) node_groups = ListField(ClusterCreateNodeGroups) scripts = ListField(ClusterCreateScript) connectors = ListField(ClusterCreateCredential) credentials = ListField(ClusterCreateCredential)
class NodeGroup(Config, ReprMixin): """Group of nodes that share the same flavor and installed services""" table_columns = ('id', 'flavor_id', 'count', '_components') table_header = ('ID', 'Flavor', 'Count', 'Components') id = Field(six.text_type, required=True, validator=Length(min=1, max=255)) count = Field(int, validator=Range(min=1, max=100)) flavor_id = Field(six.text_type) components = ListField(dict, default={})
class Service(Config): name = Field(six.text_type, required=True, validator=Length(min=1, max=255)) modes = ListField(six.text_type, validator=List(Length(min=1, max=255))) @classmethod def _describe(cls): return cls.describe().replace('\n', ', ')
class NodeGroup(Config): id = Field(six.text_type, required=True, validator=Length(min=1, max=36)) flavor_id = Field(six.text_type) count = Field(int, validator=Range(min=0, max=100)) components = ListField(Component) @classmethod def _describe(cls): return cls.describe().replace('\n', ', ')
class Flavor(Config, ReprMixin): table_columns = ('id', 'name', 'ram', 'vcpus', 'disk') table_header = ('ID', 'Name', 'RAM', 'VCPUs', 'Disk') id = Field(six.text_type, required=True) name = Field(six.text_type, required=True) disk = Field(int, required=True, help='Disk space in MB') vcpus = Field(int, required=True) ram = Field(int, required=True, help='Memory in MB') links = ListField(Link)
class Stack(Config, ReprMixin, BaseStack): table_columns = ('id', '_name', 'distro', '_description') table_header = ('ID', 'Name', 'Distro', 'Description') id = Field(six.text_type, required=True) name = Field(six.text_type, required=True) description = Field(six.text_type) links = ListField(Link) distro = Field(six.text_type, required=True, help='Distribution ID') services = ListField(StackService, required=True, help='See: :class:`StackService`') @property def _name(self): return '\n'.join(textwrap.wrap(self.name, 25)) @property def _description(self): return '\n'.join(textwrap.wrap(self.description, 30))
class DistroService(Config, ReprMixin): table_columns = ('name', 'version', '_components', '_description') table_header = ('Name', 'Version', 'Components', 'Description') name = Field(six.text_type, required=True) version = Field(six.text_type, required=True) description = Field(six.text_type, required=True) components = ListField(dict, required=True) @property def _description(self): return '\n'.join(textwrap.wrap(self.description, 30))
class StackNodeGroup(Config, ReprMixin): table_columns = ('id', 'flavor_id', 'count', 'resource_limits.min_ram', 'resource_limits.min_count', 'resource_limits.max_count') table_header = ('ID', 'Flavor', 'Count', 'Min RAM', 'Min count', 'Max Count') id = Field(six.text_type, required=True) flavor_id = Field(six.text_type, required=True) resource_limits = Field(ResourceLimits, required=True, help='See: :class:`ResourceLimits`') count = Field(int, required=True) components = ListField(dict, required=True)
class DistroDetail(Config, ReprMixin): table_columns = ('id', 'name', 'version') table_header = ('ID', 'Name', 'Version') __inherits__ = [Distro] services = ListField(DistroService, required=True, help='See: :class:`DistroService`') def display(self): display_result(self, DistroDetail, title='Distro') six.print_() display_result(self.services, DistroService, 'Services')
class StackDetail(Stack, ReprMixin, BaseStack): __inherits__ = [Stack] table_columns = ('id', 'name', 'distro', 'created', '_description', '_services', '_node_group_ids') table_header = ('ID', 'Name', 'Distro', 'Created', 'Description', 'Services', 'Node Groups') created = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'creation date') node_groups = ListField(StackNodeGroup, required=True, help='See: :class:`StackNodeGroup`') def display(self): display_result(self, StackDetail, title='Stack') if self.node_groups: display_result(self.node_groups, StackNodeGroup, title='Node Groups') self._display_components() def _display_components(self): rows = [] for group in self.node_groups: group_column = chain([group.id], repeat('')) rows.extend( no_nulls((grp, comp['name'])) for grp, comp in six.moves.zip(group_column, group.components)) print_table(rows, ('Node Group', 'Name'), title='Components') @property def _description(self): return '\n'.join(textwrap.wrap(self.description, 60)) @property def _node_group_ids(self): return '\n'.join( textwrap.wrap(', '.join(group.id for group in self.node_groups)))
class Cluster(Config, ReprMixin, BaseCluster): """Basic cluster information""" table_columns = ('id', 'name', 'status', 'stack_id', 'created') table_header = ('ID', 'Name', 'Status', 'Stack', 'Created') id = Field(six.text_type, required=True) created = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'creation date') updated = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'date last updated') name = Field(six.text_type, required=True) status = Field(six.text_type, required=True) stack_id = Field(six.text_type, required=True) cbd_version = Field(int, required=True, help='API version at which cluster was created') links = ListField(Link)
class CredentialType(Config, ReprMixin): type = Field(six.text_type, required=True) schema = Field(dict, required=True) links = ListField(Link)
class Addresses(Config, ReprMixin): public = ListField(Address, required=True, help='See: :class:`Address`') private = ListField(Address, required=True, help='See: :class:`Address`')
class StacksResponse(Config): stacks = ListField(Stack, required=True)
class ClusterCredentialsRequest(Config): credentials = ListField(ClusterUpdateCredential)
class RecommendationsResponse(Config): recommendations = ListField(Recommendations)
class WorkloadsResponse(Config): """Response from /workloads""" workloads = ListField(Workload, required=True)
class Conf(Config): one = Field(SubConf) two = ListField(SubConf) three = Field(int)
class SubConf(Config): foo = Field(SubSubConf) bar = ListField(SubSubConf) baz = Field(int)
class ClustersResponse(Config, ReprMixin): """Response from /clusters""" clusters = ListField(Cluster, required=True)
class ClusterResizeRequest(Config): """PUT data to resize cluster""" node_groups = ListField(ClusterCreateNodeGroups)
class Node(Config, ReprMixin): table_columns = ('id', 'name', 'node_group', 'status', 'public_ip', 'private_ip') table_header = ('ID', 'Name', 'Role', 'Status', 'Public IP', 'Private IP') id = Field(six.text_type, required=True) name = Field(six.text_type, required=True) created = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'creation date') updated = Field(DateTime, required=True, help=':py:class:`~datetime.datetime` corresponding to ' 'date last updated') status = Field(six.text_type, required=True) flavor_id = Field(six.text_type, required=True) addresses = Field(Addresses, required=True, help='Public and private IP addresses; See: ' ':class:`Addresses`') node_group = Field(six.text_type, required=True, help='Node group ID') components = ListField(dict, required=True, help='Components installed on this node, e.g. ' '`HiveClient`') @classmethod def display_nodes(cls, nodes): sorted_nodes = sorted(nodes, key=lambda node: node.name) display_result(sorted_nodes, Node, title='Nodes') six.print_() rows = [] for node in sorted_nodes: node_column = chain([node.name], repeat('')) rows.extend( no_nulls( [name, comp['name'], comp['nice_name'], comp.get('uri')]) for name, comp in six.moves.zip(node_column, node.components)) print_table(rows, ('Node', 'ID', 'Name', 'URI'), title='Components') @property def private_ip(self): """Private IP address on service network""" try: return self.addresses.private[0].address except IndexError: return None @property def public_ip(self): """Public IP address""" try: return self.addresses.public[0].address except IndexError: return None def _ssh(self, username, command=None, ssh_command=None): """ SSH to this node, optionally running a command and returning the output. :param username: Login user :param command: Command to execute remotely :param ssh_command: ssh command string or `list`, e.g. `ssh -F configfile` :returns: Output from running command, if a command was specified """ try: return ssh_to_host(username, self.public_ip, command=command, ssh_command=ssh_command) except subprocess.CalledProcessError as exc: msg = 'Command failed with code %d', exc.returncode LOG.error(msg) LOG.debug('Command output:\n%s', exc.output) raise error.FailedError(msg) def execute(self, username, command, ssh_command=None): """ Execute a command remotely on this node, returning the output. :param username: Login user :param command: Command to execute remotely :param ssh_command: ssh command string or `list`, e.g. `ssh -F configfile` :returns: Output from running command """ return self._ssh(username, command=command, ssh_command=ssh_command)
class ClusterCredentialsRemovalRequest(Config): remove_credentials = ListField(ClusterUpdateCredential)
class FlavorsResponse(Config): flavors = ListField(Flavor, required=True)
class NodesResponse(Config): """Response from /clusters/<cluster_id>/nodes""" nodes = ListField(Node, required=True)
class ScriptsResponse(Config): """Response from /scripts""" scripts = ListField(Script, required=True)