Added clusters to the specification, for ACS, and Google container services, others based on Kubernetes type deployments may follow. Updated feature table
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8e749b5c Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8e749b5c Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8e749b5c Branch: refs/heads/trunk Commit: 8e749b5cd385b3e04ba3987c21ac217c0ff1e1ce Parents: 6d83dc0 Author: anthony-shaw <[email protected]> Authored: Wed Dec 23 17:26:29 2015 +1100 Committer: anthony-shaw <[email protected]> Committed: Wed Dec 23 17:26:29 2015 +1100 ---------------------------------------------------------------------- .../generate_provider_feature_matrix_table.py | 11 +- docs/container/_supported_methods.rst | 12 +- libcloud/container/base.py | 135 ++++++++++++++++++- libcloud/container/drivers/docker.py | 65 ++++----- libcloud/container/drivers/dummy.py | 1 + libcloud/container/drivers/joyent.py | 1 + 6 files changed, 175 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/contrib/generate_provider_feature_matrix_table.py ---------------------------------------------------------------------- diff --git a/contrib/generate_provider_feature_matrix_table.py b/contrib/generate_provider_feature_matrix_table.py index 58d571f..3995eb2 100755 --- a/contrib/generate_provider_feature_matrix_table.py +++ b/contrib/generate_provider_feature_matrix_table.py @@ -96,7 +96,9 @@ BASE_API_METHODS = { 'delete_zone', 'delete_record'], 'container': ['install_image', 'list_images', 'deploy_container', 'get_container', 'start_container', 'stop_container', - 'restart_container', 'delete_container'] + 'restart_container', 'delete_container', 'list_containers', + 'list_locations', 'create_cluster', 'delete_cluster', + 'list_clusters'] } FRIENDLY_METHODS_NAMES = { @@ -173,10 +175,15 @@ FRIENDLY_METHODS_NAMES = { 'list_images': 'list images', 'deploy_container': 'deploy container', 'get_container': 'get container', + 'list_containers': 'list containers', 'start_container': 'start container', 'stop_container': 'stop container', 'restart_container': 'restart container', - 'delete_container': 'delete container' + 'delete_container': 'delete container', + 'list_locations': 'list locations', + 'create_cluster': 'create cluster', + 'delete_cluster': 'delete cluster', + 'list_clusters': 'list clusters' } } http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/docs/container/_supported_methods.rst ---------------------------------------------------------------------- diff --git a/docs/container/_supported_methods.rst b/docs/container/_supported_methods.rst index daceb3e..1a8c963 100644 --- a/docs/container/_supported_methods.rst +++ b/docs/container/_supported_methods.rst @@ -1,11 +1,11 @@ .. NOTE: This file has been generated automatically using generate_provider_feature_matrix_table.py script, don't manually edit it -========= ============= =========== ================ ============= =============== ============== ================= ================ -Provider install image list images deploy container get container start container stop container restart container delete container -========= ============= =========== ================ ============= =============== ============== ================= ================ -`Docker`_ yes yes yes yes yes yes yes yes -`Joyent`_ yes yes yes yes yes yes yes yes -========= ============= =========== ================ ============= =============== ============== ================= ================ +========= ============= =========== ================ ============= =============== ============== ================= ================ =============== ============== ============== ============== ============= +Provider install image list images deploy container get container start container stop container restart container delete container list containers list locations create cluster delete cluster list clusters +========= ============= =========== ================ ============= =============== ============== ================= ================ =============== ============== ============== ============== ============= +`Docker`_ yes yes yes yes yes yes yes yes yes no no no no +`Joyent`_ yes yes yes yes yes yes yes yes yes no no no no +========= ============= =========== ================ ============= =============== ============== ================= ================ =============== ============== ============== ============== ============= .. _`Docker`: http://docker.io .. _`Joyent`: http://joyent.com http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/libcloud/container/base.py ---------------------------------------------------------------------- diff --git a/libcloud/container/base.py b/libcloud/container/base.py index 0d26035..ec28308 100644 --- a/libcloud/container/base.py +++ b/libcloud/container/base.py @@ -21,6 +21,7 @@ from libcloud.common.base import ConnectionUserAndKey, BaseDriver __all__ = [ 'Container', 'ContainerImage', + 'ContainerCluster', 'ContainerDriver' ] @@ -30,7 +31,8 @@ class Container(object): Container. """ - def __init__(self, id, name, image, state, driver, extra=None): + def __init__(self, id, name, image, state, + ip_addresses, driver, extra=None): """ :param id: Container id. :type id: ``str`` @@ -44,6 +46,9 @@ class Container(object): :param state: The state of the container, e.g. running :type state: :class:`libcloud.container.types.ContainerState` + :param ip_addresses: A list of IP addresses for this container + :type ip_addresses: ``list`` of ``str`` + :param driver: ContainerDriver instance. :type driver: :class:`ContainerDriver` @@ -54,6 +59,7 @@ class Container(object): self.name = name self.image = image self.state = state + self.ip_addresses = ip_addresses self.driver = driver self.extra = extra or {} @@ -120,6 +126,73 @@ class ContainerImage(object): (self.id, self.name, self.driver.name)) +class ContainerCluster(object): + """ + A cluster group for containers + """ + + def __init__(self, id, name, driver, extra=None): + """ + :param id: Container Image id. + :type id: ``str`` + + :param name: The name of the image. + :type name: ``str`` + + :param driver: ContainerDriver instance. + :type driver: :class:`ContainerDriver` + + :param extra: (optional) Extra attributes (driver specific). + :type extra: ``dict`` + """ + self.id = str(id) if id else None + self.name = name + self.driver = driver + self.extra = extra or {} + + def list_containers(self): + return self.driver.list_containers(cluster=self) + + def __repr__(self): + return ('<ContainerCluster: id=%s, name=%s, provider=%s ...>' % + (self.id, self.name, self.driver.name)) + + +class ClusterLocation(object): + """ + A physical location where clusters can be. + + >>> from libcloud.container.drivers.dummy import DummyNodeDriver + >>> driver = DummyNodeDriver(0) + >>> location = driver.list_locations()[0] + >>> location.country + 'US' + """ + + def __init__(self, id, name, country, driver): + """ + :param id: Location ID. + :type id: ``str`` + + :param name: Location name. + :type name: ``str`` + + :param country: Location country. + :type country: ``str`` + + :param driver: Driver this location belongs to. + :type driver: :class:`.NodeDriver` + """ + self.id = str(id) + self.name = name + self.country = country + self.driver = driver + + def __repr__(self): + return (('<ClusterLocation: id=%s, name=%s, country=%s, driver=%s>') + % (self.id, self.name, self.country, self.driver.name)) + + class ContainerDriver(BaseDriver): """ A base ContainerDriver class to derive from @@ -129,6 +202,10 @@ class ContainerDriver(BaseDriver): connectionCls = ConnectionUserAndKey name = None website = None + supports_clusters = False + """ + Whether the driver supports containers being deployed into clusters + """ def __init__(self, key, secret=None, secure=True, host=None, port=None, **kwargs): @@ -176,19 +253,23 @@ class ContainerDriver(BaseDriver): raise NotImplementedError( 'list_images not implemented for this driver') - def list_containers(self, image=None): + def list_containers(self, image=None, cluster=None): """ List the deployed container images :param image: Filter to containers with a certain image :type image: :class:`ContainerImage` + :param cluster: Filter to containers in a cluster + :type cluster: :class:`ContainerCluster` + :rtype: ``list`` of :class:`Container` """ raise NotImplementedError( 'list_containers not implemented for this driver') - def deploy_container(self, name, image, parameters=None, start=True): + def deploy_container(self, name, image, cluster=None, + parameters=None, start=True): """ Deploy an installed container image @@ -198,6 +279,9 @@ class ContainerDriver(BaseDriver): :param image: The container image to deploy :type image: :class:`ContainerImage` + :param cluster: The cluster to deploy to, None is default + :type cluster: :class:`ContainerCluster` + :param parameters: Container Image parameters :type parameters: ``str`` @@ -268,3 +352,48 @@ class ContainerDriver(BaseDriver): """ raise NotImplementedError( 'delete_container not implemented for this driver') + + def list_locations(self): + """ + Get a list of potential locations to deploy clusters into + + :rtype: ``list`` of :class:`ClusterLocation` + """ + raise NotImplementedError( + 'list_locations not implemented for this driver') + + def create_cluster(self, name, location=None): + """ + Create a container cluster + + :param name: The name of the cluster + :type name: ``str`` + + :param location: The location to create the cluster in + :type location: :class:`ClusterLocation` + + :rtype: :class:`ContainerCluster` + """ + raise NotImplementedError( + 'create_cluster not implemented for this driver') + + def delete_cluster(self, cluster): + """ + Delete a cluster + + :rtype: ``list`` of :class:`ClusterLocation` + """ + raise NotImplementedError( + 'delete_cluster not implemented for this driver') + + def list_clusters(self, location=None): + """ + Get a list of potential locations to deploy clusters into + + :param location: The location to search in + :type location: :class:`ClusterLocation` + + :rtype: ``list`` of :class:`ContainerCluster` + """ + raise NotImplementedError( + 'list_clusters not implemented for this driver') http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/libcloud/container/drivers/docker.py ---------------------------------------------------------------------- diff --git a/libcloud/container/drivers/docker.py b/libcloud/container/drivers/docker.py index e1c2043..3e456d5 100644 --- a/libcloud/container/drivers/docker.py +++ b/libcloud/container/drivers/docker.py @@ -128,6 +128,7 @@ class DockerContainerDriver(ContainerDriver): name = 'Docker' website = 'http://docker.io' connectionCls = DockerConnection + supports_clusters = False def __init__(self, key=None, secret=None, secure=False, host='localhost', port=4243, key_file=None, cert_file=None): @@ -356,35 +357,7 @@ class DockerContainerDriver(ContainerDriver): result = self.connection.request("/containers/%s/json" % id).object - name = result.get('Name').strip('/') - if result['State']['Running']: - state = ContainerState.RUNNING - else: - state = ContainerState.STOPPED - image = result.get('Image') - extra = { - 'volumes': result.get('Volumes'), - 'env': result.get('Config', {}).get('Env'), - 'ports': result.get('ExposedPorts'), - 'network_settings': result.get('NetworkSettings', {}) - } - container_id = result.get('Id') - if not container_id: - container_id = result.get('ID', '') - container = (Container( - id=container_id, - name=name, - image=ContainerImage( - id=None, - path=image, - name=image, - version=None, - driver=self.connection.driver - ), - state=state, - driver=self.connection.driver, - extra=extra)) - return container + return self._to_container(result) def start_container(self, container): """ @@ -586,29 +559,43 @@ class DockerContainerDriver(ContainerDriver): Convert container in Container instances """ try: - name = data.get('Names')[0].strip('/') + name = data.get('Name').strip('/') except: - name = data.get('Id') - if 'Exited' in data.get('Status'): + try: + name = data.get('Names')[0].strip('/') + except: + name = data.get('Id') + state = data.get('State') + status = data.get('Status', + state.get('Status') + if state is not None else None) + if 'Exited' in status: state = ContainerState.STOPPED - elif data.get('Status').startswith('Up '): + elif status.startswith('Up '): state = ContainerState.RUNNING else: state = ContainerState.STOPPED image = data.get('Image') - ports = json.dumps(data.get('Ports', {})) + ports = data.get('Ports', []) + created = data.get('Created') + if isinstance(created, float): + created = ts_to_str(created) extra = { 'id': data.get('Id'), 'status': data.get('Status'), - 'created': ts_to_str(data.get('Created')), + 'created': created, 'image': image, 'ports': ports, 'command': data.get('Command'), 'sizerw': data.get('SizeRw'), 'sizerootfs': data.get('SizeRootFs'), } - - node = (Container( + ips = [] + if ports is not None: + for port in ports: + if port.get('IP') is not None: + ips.append(port.get('IP')) + return Container( id=data['Id'], name=name, image=ContainerImage( @@ -618,10 +605,10 @@ class DockerContainerDriver(ContainerDriver): version=None, driver=self.connection.driver ), + ip_addresses=ips, state=state, driver=self.connection.driver, - extra=extra)) - return node + extra=extra) def _get_api_version(self): """ http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/libcloud/container/drivers/dummy.py ---------------------------------------------------------------------- diff --git a/libcloud/container/drivers/dummy.py b/libcloud/container/drivers/dummy.py index b3f5db5..2c99259 100644 --- a/libcloud/container/drivers/dummy.py +++ b/libcloud/container/drivers/dummy.py @@ -28,6 +28,7 @@ class DummyContainerDriver(ContainerDriver): name = 'Dummy Container Provider' website = 'http://example.com' + supports_clusters = False def __init__(self, api_key, api_secret): """ http://git-wip-us.apache.org/repos/asf/libcloud/blob/8e749b5c/libcloud/container/drivers/joyent.py ---------------------------------------------------------------------- diff --git a/libcloud/container/drivers/joyent.py b/libcloud/container/drivers/joyent.py index f236808..e380f6a 100644 --- a/libcloud/container/drivers/joyent.py +++ b/libcloud/container/drivers/joyent.py @@ -34,6 +34,7 @@ class JoyentContainerDriver(DockerContainerDriver): name = 'Joyent' website = 'http://joyent.com' connectionCls = DockerConnection + supports_clusters = False def __init__(self, key=None, secret=None, secure=False, host='localhost', port=2376, key_file=None, cert_file=None):
