You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by GitBox <gi...@apache.org> on 2019/12/27 13:47:56 UTC

[GitHub] [libcloud] pockerman commented on a change in pull request #1395: WIP: Add LXD driver & tests

pockerman commented on a change in pull request #1395: WIP: Add LXD driver & tests
URL: https://github.com/apache/libcloud/pull/1395#discussion_r361666959
 
 

 ##########
 File path: libcloud/container/drivers/lxd.py
 ##########
 @@ -0,0 +1,1269 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64
+import re
+import os
+
+
+try:
+    import simplejson as json
+except Exception:
+    import json
+
+from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import b
+
+from libcloud.common.base import JsonResponse, ConnectionUserAndKey
+from libcloud.common.base import KeyCertificateConnection
+from libcloud.common.types import InvalidCredsError
+
+from libcloud.container.base import (Container, ContainerDriver,
+                                     ContainerImage)
+from libcloud.common.exceptions import BaseHTTPError
+
+from libcloud.compute.base import StorageVolume
+
+from libcloud.container.providers import Provider
+from libcloud.container.types import ContainerState
+
+# Acceptable success strings comping from LXD API
+LXD_API_SUCCESS_STATUS = ['Success']
+LXD_API_STATE_ACTIONS = ['stop', 'start', 'restart', 'freeze', 'unfreeze']
+LXD_API_IMAGE_SOURCE_TYPE = ["image", "migration", "copy", "none"]
+
+# the wording used by LXD to indicate that an error
+# occurred for a request
+LXD_ERROR_STATUS_RESP = 'error'
+
+
+# helpers
+def strip_http_prefix(host):
+    # strip the prefix
+    prefixes = ['http://', 'https://']
+    for prefix in prefixes:
+        if host.startswith(prefix):
+            host = host.strip(prefix)
+    return host
+
+
+def check_certificates(key_file, cert_file, **kwargs):
+    """
+    Basic checks for the provided certificates in LXDtlsConnection
+    """
+
+    # there is no point attempting to connect if either is missing
+    if key_file is None or cert_file is None:
+        raise InvalidCredsError("TLS Connection requires specification "
+                                "of a key file and a certificate file")
+
+    # if they are not none they may be empty strings
+    # or certificates that are not appropriate
+    if key_file == '' or cert_file == '':
+        raise InvalidCredsError("TLS Connection requires specification "
+                                "of a key file and a certificate file")
+
+    # if none of the above check the types
+    if 'key_files_allowed' in kwargs.keys():
+        key_file_suffix = key_file.split('.')
+
+        if key_file_suffix[-1] not in kwargs['key_files_allowed']:
+            raise InvalidCredsError("Valid key files are: " +
+                                    str(kwargs['key_files_allowed']) +
+                                    "you provided: " + key_file_suffix[-1])
+
+            # if none of the above check the types
+    if 'cert_files_allowed' in kwargs.keys():
+        cert_file_suffix = cert_file.split('.')
+
+        if cert_file_suffix[-1] not in kwargs['cert_files_allowed']:
+            raise InvalidCredsError("Valid certification files are: " +
+                                    str(kwargs['cert_files_allowed']) +
+                                    "you provided: " + cert_file_suffix[-1])
+
+    # if all these are good check the paths
+    keypath = os.path.expanduser(key_file)
+    is_file_path = os.path.exists(keypath) and os.path.isfile(keypath)
+    if not is_file_path:
+        raise InvalidCredsError('You need a key file to authenticate with '
+                                'LXD tls. This can be found in the server.')
+
+    certpath = os.path.expanduser(cert_file)
+    is_file_path = os.path.exists(certpath) and os.path.isfile(certpath)
+    if not is_file_path:
+        raise InvalidCredsError('You need a certificate file to '
+                                'authenticate with LXD tls. '
+                                'This can be found in the server.')
+
+
+def assert_response(response_dict, status_code=200):
+
+    # if the type of the response is an error
+    if response_dict['type'] == LXD_ERROR_STATUS_RESP:
+        # an error returned
+        raise LXDAPIException(message="response type is error")
+
+    # anything else apart from the status_code given should be treated as error
+    if response_dict['status_code'] != status_code:
+        # we have an unknown error
+        msg = "Status code should be {0}\
+         but is {1}".format(status_code, response_dict['status_code'])
+        raise LXDAPIException(message=msg)
+
+
+class LXDAPIException(Exception):
+    """
+    Basic exception to be thrown when LXD API
+    returns with some kind of error
+    """
+
+    def __init__(self, message="Unknown Error Occurred"):
+        self.message = message
+
+        super(LXDAPIException, self).__init__(message)
+
+    def __str__(self):
+        return self.message
+
+
+class LXDStoragePool(object):
+    """
+    Utility class representing an LXD storage pool
+    https://lxd.readthedocs.io/en/latest/storage/
+    """
+    def __init__(self, name, driver, used_by, config, managed):
+
+        # the name of the storage pool
+        self.name = name
+
+        # the driver (or type of storage pool). e.g. ‘zfs’ or ‘btrfs’, etc.
+        self.driver = driver
+
+        # which containers (by API endpoint /1.0/containers/<name>)
+        # are using this storage-pool.
+        self.used_by = used_by
+
+        # a dictionary with some information about the storage-pool.
+        # e.g. size, source (path), volume.size, etc.
+        self.config = config
+
+        # Boolean that indicates whether LXD manages the pool or not.
+        self.managed = managed
+
+
+class LXDServerInfo(object):
+    """
+    Wraps the response form /1.0
+    """
+
+    @classmethod
+    def build_from_response(cls, metadata):
+
+        server_info = LXDServerInfo()
+        server_info.api_extensions = metadata.get("api_extensions", None)
+        server_info.api_status = metadata.get("api_status", None)
+        server_info.api_version = metadata.get("api_version", None)
+        server_info.auth = metadata.get("auth", None)
+        server_info.config = metadata.get("config", None)
+        server_info.environment = metadata.get("environment", None)
+        server_info.public = metadata.get("public", None)
+        return server_info
+
+    def __init__(self):
+
+        # List of API extensions added after
+        # the API was marked stable
+        self.api_extensions = None
+
+        # API implementation status
+        # (one of, development, stable or deprecated)
+        self.api_status = None
+
+        # The API version as a string
+        self.api_version = None
+
+        # Authentication state,
+        # one of "guest", "untrusted" or "trusted"
+        self.auth = None
+
+        self.config = None
+
+        # Various information about the host (OS, kernel, ...)
+        self.environment = None
+
+        self.public = None
+
+    def __str__(self):
+        return str(self.api_extensions) + str(self.api_status) + \
+            str(self.api_version) + str(self.auth) + str(self.config) + \
+            str(self.environment) + \
+            str(self.public)
+
+
+class LXDResponse(JsonResponse):
+    valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
+                            httplib.NO_CONTENT]
+
+    def parse_body(self):
+
+        if len(self.body) == 0 and not self.parse_zero_length_body:
+            return self.body
+
+        try:
+            # error responses are tricky in Docker. Eg response could be
+            # an error, but response status could still be 200
+            content_type = self.headers.get('content-type', 'application/json')
+            if content_type == 'application/json' or content_type == '':
+                if self.headers.get('transfer-encoding') == 'chunked' and \
+                        'fromImage' in self.request.url:
+                    body = [json.loads(chunk) for chunk in
 
 Review comment:
   Basically, this is a copy verbatim from the Docker driver which means I should work more on it at some point. PyCharm shows 138 implementations of ```JsonResponse``` we need someone brave :) :)  

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services