Exemple #1
0
    def create(cls, client, config, wait=False):
        """Create a new container config."""
        response = client.api.containers.post(json=config)

        if wait:
            Operation.wait_for_operation(client, response.json()['operation'])
        return cls(client, name=config['name'])
Exemple #2
0
 def rename(self, new_name, wait=False):
     """Rename a snapshot."""
     response = self.api.post(json={'name': new_name})
     if wait:
         Operation.wait_for_operation(self.client,
                                      response.json()['operation'])
     self.name = new_name
Exemple #3
0
    def rename(self, name, wait=False):
        """Rename a container."""
        response = self.api.post(json={'name': name})

        if wait:
            Operation.wait_for_operation(self.client,
                                         response.json()['operation'])
        self.name = name
Exemple #4
0
    def delete(self, wait=False):
        """Delete an object from the server."""
        response = self.api.delete()

        if response.json()['type'] == 'async' and wait:
            Operation.wait_for_operation(self.client,
                                         response.json()['operation'])
        self.client = None
Exemple #5
0
    def create(cls, client, container, name, stateful=False, wait=False):
        response = client.api.containers[container.name].snapshots.post(
            json={
                'name': name,
                'stateful': stateful
            })

        snapshot = cls(client, container=container, name=name)
        if wait:
            Operation.wait_for_operation(client, response.json()['operation'])
        return snapshot
Exemple #6
0
 def _set_state(self, state, timeout=30, force=True, wait=False):
     response = self.api.state.put(json={
         'action': state,
         'timeout': timeout,
         'force': force
     })
     if wait:
         Operation.wait_for_operation(self.client,
                                      response.json()['operation'])
         if 'status' in self.__dirty__:
             del self.__dirty__[self.__dirty__.index('status')]
         self.sync()
Exemple #7
0
    def create(cls, client, image_data, public=False, wait=False):
        """Create an image."""
        fingerprint = hashlib.sha256(image_data).hexdigest()

        headers = {}
        if public:
            headers['X-LXD-Public'] = '1'
        response = client.api.images.post(data=image_data, headers=headers)

        if wait:
            Operation.wait_for_operation(client, response.json()['operation'])
        return cls(client, fingerprint=fingerprint)
Exemple #8
0
def _image_create_from_config(client, config, wait=False):
    """ Create an image from the given configuration.

    See: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-6
    """
    response = client.api.images.post(json=config)
    if wait:
        Operation.wait_for_operation(client, response.json()['operation'])

        return Operation.get(client, response.json()['operation'])

    return response.json()['operation']
Exemple #9
0
    def save(self, wait=False):
        """Save data to the server.

        This method should write the new data to the server via marshalling.
        It should be a no-op when the object is not dirty, to prevent needless
        I/O.
        """
        marshalled = self.marshall()
        response = self.api.put(json=marshalled)

        if response.json()['type'] == 'async' and wait:
            Operation.wait_for_operation(self.client,
                                         response.json()['operation'])
        self.__dirty__.clear()
Exemple #10
0
    def publish(self, public=False, wait=False):
        """Publish a snapshot as an image.

        If wait=True, an Image is returned.

        This functionality is currently broken in LXD. Please see
        https://github.com/lxc/lxd/issues/2201 - The implementation
        here is mostly a guess. Once that bug is fixed, we can verify
        that this works, or file a bug to fix it appropriately.
        """
        data = {
            'public': public,
            'source': {
                'type': 'snapshot',
                'name': '{}/{}'.format(self.container.name, self.name),
            }
        }

        response = self.client.api.images.post(json=data)
        if wait:
            operation = Operation.wait_for_operation(
                self.client,
                response.json()['operation'])
            return self.client.images.get(operation.metadata['fingerprint'])
Exemple #11
0
    def publish(self, public=False, wait=False):
        """Publish a container as an image.

        The container must be stopped in order publish it as an image. This
        method does not enforce that constraint, so a LXDAPIException may be
        raised if this method is called on a running container.

        If wait=True, an Image is returned.
        """
        data = {
            'public': public,
            'source': {
                'type': 'container',
                'name': self.name,
            }
        }

        response = self.client.api.images.post(json=data)
        if wait:
            operation = Operation.wait_for_operation(
                self.client,
                response.json()['operation'])

            return self.client.images.get(operation.metadata['fingerprint'])
Exemple #12
0
    def execute(
        self,
        commands,
        environment={},
        encoding=None,
        decode=True,
        stdin_payload=None,
        stdin_encoding="utf-8",
        stdout_handler=None,
        stderr_handler=None,
    ):
        """Execute a command on the container.

        In pylxd 2.2, this method will be renamed `execute` and the existing
        `execute` method removed.

        :param commands: The command and arguments as a list of strings
        :type commands: [str]
        :param environment: The environment variables to pass with the command
        :type environment: {str: str}
        :param encoding: The encoding to use for stdout/stderr if the param
            decode is True.  If encoding is None, then no override is
            performed and whatever the existing encoding from LXD is used.
        :type encoding: str
        :param decode: Whether to decode the stdout/stderr or just return the
            raw buffers.
        :type decode: bool
        :param stdin_payload: Payload to pass via stdin
        :type stdin_payload: Can be a file, string, bytearray, generator or
            ws4py Message object
        :param stdin_encoding: Encoding to pass text to stdin (default utf-8)
        :param stdout_handler: Callable than receive as first parameter each
            message recived via stdout
        :type stdout_handler: Callable[[str], None]
        :param stderr_handler: Callable than receive as first parameter each
            message recived via stderr
        :type stderr_handler: Callable[[str], None]
        :raises ValueError: if the ws4py library is not installed.
        :returns: A tuple of `(exit_code, stdout, stderr)`
        :rtype: _ContainerExecuteResult() namedtuple
        """
        if not _ws4py_installed:
            raise ValueError(
                'This feature requires the optional ws4py library.')
        if isinstance(commands, six.string_types):
            raise TypeError("First argument must be a list.")
        response = self.api['exec'].post(
            json={
                'command': commands,
                'environment': environment,
                'wait-for-websocket': True,
                'interactive': False,
            })

        fds = response.json()['metadata']['metadata']['fds']
        operation_id = \
            Operation.extract_operation_id(response.json()['operation'])
        parsed = parse.urlparse(
            self.client.api.operations[operation_id].websocket._api_endpoint)

        with managers.web_socket_manager(WebSocketManager()) as manager:
            stdin = _StdinWebsocket(self.client.websocket_url,
                                    payload=stdin_payload,
                                    encoding=stdin_encoding)
            stdin.resource = '{}?secret={}'.format(parsed.path, fds['0'])
            stdin.connect()
            stdout = _CommandWebsocketClient(manager,
                                             self.client.websocket_url,
                                             encoding=encoding,
                                             decode=decode,
                                             handler=stdout_handler)
            stdout.resource = '{}?secret={}'.format(parsed.path, fds['1'])
            stdout.connect()
            stderr = _CommandWebsocketClient(manager,
                                             self.client.websocket_url,
                                             encoding=encoding,
                                             decode=decode,
                                             handler=stderr_handler)
            stderr.resource = '{}?secret={}'.format(parsed.path, fds['2'])
            stderr.connect()

            manager.start()

            # watch for the end of the command:
            while True:
                operation = self.client.operations.get(operation_id)
                if 'return' in operation.metadata:
                    break
                time.sleep(.5)  # pragma: no cover

            try:
                stdin.close()
            except BrokenPipeError:
                pass

            stdout.finish_soon()
            stderr.finish_soon()
            manager.close_all()

            while not stdout.finished or not stderr.finished:
                time.sleep(.1)  # progma: no cover

            manager.stop()
            manager.join()

            return _ContainerExecuteResult(operation.metadata['return'],
                                           stdout.data, stderr.data)
Exemple #13
0
    def execute(
        self,
        commands,
        environment=None,
        encoding=None,
        decode=True,
        stdin_payload=None,
        stdin_encoding="utf-8",
        stdout_handler=None,
        stderr_handler=None,
    ):
        """Execute a command on the instance. stdout and stderr are buffered if
        no handler is given.

        :param commands: The command and arguments as a list of strings
        :type commands: [str]
        :param environment: The environment variables to pass with the command
        :type environment: {str: str}
        :param encoding: The encoding to use for stdout/stderr if the param
            decode is True.  If encoding is None, then no override is
            performed and whatever the existing encoding from LXD is used.
        :type encoding: str
        :param decode: Whether to decode the stdout/stderr or just return the
            raw buffers.
        :type decode: bool
        :param stdin_payload: Payload to pass via stdin
        :type stdin_payload: Can be a file, string, bytearray, generator or
            ws4py Message object
        :param stdin_encoding: Encoding to pass text to stdin (default utf-8)
        :param stdout_handler: Callable than receive as first parameter each
            message received via stdout
        :type stdout_handler: Callable[[str], None]
        :param stderr_handler: Callable than receive as first parameter each
            message received via stderr
        :type stderr_handler: Callable[[str], None]
        :raises ValueError: if the ws4py library is not installed.
        :returns: A tuple of `(exit_code, stdout, stderr)`
        :rtype: _InstanceExecuteResult() namedtuple
        """
        if not _ws4py_installed:
            raise ValueError(
                "This feature requires the optional ws4py library.")
        if isinstance(commands, str):
            raise TypeError("First argument must be a list.")
        if environment is None:
            environment = {}

        response = self.api["exec"].post(
            json={
                "command": commands,
                "environment": environment,
                "wait-for-websocket": True,
                "interactive": False,
            })

        fds = response.json()["metadata"]["metadata"]["fds"]
        operation_id = Operation.extract_operation_id(
            response.json()["operation"])
        parsed = parse.urlparse(
            self.client.api.operations[operation_id].websocket._api_endpoint)

        with managers.web_socket_manager(WebSocketManager()) as manager:
            stdin = _StdinWebsocket(
                self.client.websocket_url,
                payload=stdin_payload,
                encoding=stdin_encoding,
            )
            stdin.resource = "{}?secret={}".format(parsed.path, fds["0"])
            stdin.connect()
            stdout = _CommandWebsocketClient(
                manager,
                self.client.websocket_url,
                encoding=encoding,
                decode=decode,
                handler=stdout_handler,
            )
            stdout.resource = "{}?secret={}".format(parsed.path, fds["1"])
            stdout.connect()
            stderr = _CommandWebsocketClient(
                manager,
                self.client.websocket_url,
                encoding=encoding,
                decode=decode,
                handler=stderr_handler,
            )
            stderr.resource = "{}?secret={}".format(parsed.path, fds["2"])
            stderr.connect()

            manager.start()

            # watch for the end of the command:
            while True:
                operation = self.client.operations.get(operation_id)
                if "return" in operation.metadata:
                    break
                time.sleep(0.5)  # pragma: no cover

            try:
                stdin.close()
            except BrokenPipeError:
                pass

            stdout.finish_soon()
            stderr.finish_soon()
            try:
                manager.close_all()
            except BrokenPipeError:
                pass

            while not stdout.finished or not stderr.finished:
                time.sleep(0.1)  # progma: no cover

            manager.stop()
            manager.join()

            return _InstanceExecuteResult(operation.metadata["return"],
                                          stdout.data, stderr.data)
Exemple #14
0
    def execute(
            self, commands, environment={}, encoding=None, decode=True,
            stdin_payload=None, stdin_encoding="utf-8",
            stdout_handler=None, stderr_handler=None
    ):
        """Execute a command on the container.

        In pylxd 2.2, this method will be renamed `execute` and the existing
        `execute` method removed.

        :param commands: The command and arguments as a list of strings
        :type commands: [str]
        :param environment: The environment variables to pass with the command
        :type environment: {str: str}
        :param encoding: The encoding to use for stdout/stderr if the param
            decode is True.  If encoding is None, then no override is
            performed and whatever the existing encoding from LXD is used.
        :type encoding: str
        :param decode: Whether to decode the stdout/stderr or just return the
            raw buffers.
        :type decode: bool
        :param stdin_payload: Payload to pass via stdin
        :type stdin_payload: Can be a file, string, bytearray, generator or
            ws4py Message object
        :param stdin_encoding: Encoding to pass text to stdin (default utf-8)
        :param stdout_handler: Callable than receive as first parameter each
            message recived via stdout
        :type stdout_handler: Callable[[str], None]
        :param stderr_handler: Callable than receive as first parameter each
            message recived via stderr
        :type stderr_handler: Callable[[str], None]
        :raises ValueError: if the ws4py library is not installed.
        :returns: A tuple of `(exit_code, stdout, stderr)`
        :rtype: _ContainerExecuteResult() namedtuple
        """
        if not _ws4py_installed:
            raise ValueError(
                'This feature requires the optional ws4py library.')
        if isinstance(commands, six.string_types):
            raise TypeError("First argument must be a list.")
        response = self.api['exec'].post(json={
            'command': commands,
            'environment': environment,
            'wait-for-websocket': True,
            'interactive': False,
        })

        fds = response.json()['metadata']['metadata']['fds']
        operation_id = \
            Operation.extract_operation_id(response.json()['operation'])
        parsed = parse.urlparse(
            self.client.api.operations[operation_id].websocket._api_endpoint)

        with managers.web_socket_manager(WebSocketManager()) as manager:
            stdin = _StdinWebsocket(
                self.client.websocket_url, payload=stdin_payload,
                encoding=stdin_encoding
            )
            stdin.resource = '{}?secret={}'.format(parsed.path, fds['0'])
            stdin.connect()
            stdout = _CommandWebsocketClient(
                manager, self.client.websocket_url,
                encoding=encoding, decode=decode, handler=stdout_handler)
            stdout.resource = '{}?secret={}'.format(parsed.path, fds['1'])
            stdout.connect()
            stderr = _CommandWebsocketClient(
                manager, self.client.websocket_url,
                encoding=encoding, decode=decode, handler=stderr_handler)
            stderr.resource = '{}?secret={}'.format(parsed.path, fds['2'])
            stderr.connect()

            manager.start()

            # watch for the end of the command:
            while True:
                operation = self.client.operations.get(operation_id)
                if 'return' in operation.metadata:
                    break
                time.sleep(.5)  # pragma: no cover

            while len(manager.websockets.values()) > 0:
                time.sleep(.1)  # pragma: no cover

            manager.stop()
            manager.join()

            return _ContainerExecuteResult(
                operation.metadata['return'], stdout.data, stderr.data)