1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
|
Containers
==========
`Container` objects are the core of LXD. Containers can be created,
updated, and deleted. Most of the methods for operating on the
container itself are asynchronous, but many of the methods for getting
information about the container are synchronous.
Manager methods
---------------
Containers can be queried through the following client manager
methods:
- `exists(name)` - Returns `boolean` indicating if the container exists.
- `all()` - Retrieve all containers.
- `get()` - Get a specific container, by its name.
- `create(config, wait=False)` - Create a new container. This method
requires the container config as the first parameter.
The config itself is beyond the scope of this documentation. Please
refer to the LXD documentation for more information. This method
will also return immediately, unless `wait` is `True`.
Container attributes
--------------------
For more information about the specifics of these attributes, please see
the LXD documentation.
- `architecture` - The container architecture.
- `config` - The container config
- `created_at` - The time the container was created
- `devices` - The devices for the container
- `ephemeral` - Whether the container is ephemeral
- `expanded_config` - An expanded version of the config
- `expanded_devices` - An expanded version of devices
- `name` - (Read only) The name of the container. This attribute serves as the
primary identifier of a container
- `description` - A description given to the container
- `profiles` - A list of profiles applied to the container
- `status` - (Read only) A string representing the status of the container
- `last_used_at` - (Read only) when the container was last used
- `status_code` - (Read only) A LXD status code of the container
- `stateful` - (Read only) Whether the container is stateful
Container methods
-----------------
- `rename` - Rename a container. Because `name` is the key, it cannot be
renamed by simply changing the name of the container as an attribute
and calling `save`. The new name is the first argument and, as the method
is asynchronous, you may pass `wait=True` as well.
- `save` - Update container's configuration
- `state` - Get the expanded state of the container.
- `start` - Start the container
- `stop` - Stop the container
- `restart` - Restart the container
- `freeze` - Suspend the container
- `unfreeze` - Resume the container
- `execute` - Execute a command on the container. The first argument is
a list, in the form of `subprocess.Popen` with each item of the command
as a separate item in the list. Returns a tuple of `(exit_code, stdout, stderr)`.
This method will block while the command is executed.
- `raw_interactive_execute` - Execute a command on the container. It will return
an url to an interactive websocket and the execution only starts after a client connected to the websocket.
- `migrate` - Migrate the container. The first argument is a client
connection to the destination server. This call is asynchronous, so
``wait=True`` is optional. The container on the new client is returned. If
``live=True`` is passed to the function call, then the container is live
migrated (see the LXD documentation for further details).
- `publish` - Publish the container as an image. Note the container must be stopped
in order to use this method. If `wait=True` is passed, then the image is returned.
- `restore_snapshot` - Restore a snapshot by name.
Examples
--------
If you'd only like to fetch a single container by its name...
.. code-block:: python
>>> client.containers.get('my-container')
<container.Container at 0x7f95d8af72b0>
If you're looking to operate on all containers of a LXD instance, you can
get a list of all LXD containers with `all`.
.. code-block:: python
>>> client.containers.all()
[<container.Container at 0x7f95d8af72b0>,]
In order to create a new :class:`~container.Container`, a container
config dictionary is needed, containing a name and the source. A create
operation is asynchronous, so the operation will take some time. If you'd
like to wait for the container to be created before the command returns,
you'll pass `wait=True` as well.
.. code-block:: python
>>> config = {'name': 'my-container', 'source': {'type': 'none'}}
>>> container = client.containers.create(config, wait=False)
>>> container
<container.Container at 0x7f95d8af72b0>
If you were to use an actual image source, you would be able to operate
on the container, starting, stopping, snapshotting, and deleting the
container.
.. code-block:: python
>>> config = {'name': 'my-container', 'source': {'type': 'image', 'alias': 'ubuntu/trusty'}}
>>> container = client.containers.create(config, wait=True)
>>> container.start()
>>> container.freeze()
>>> container.delete()
Config line with a specific image source and a profile.
.. code-block:: python
>>> config = {'name': 'my-container', 'source': {'type': 'image', "mode": "pull", "server":
"https://cloud-images.ubuntu.com/daily", "protocol": "simplestreams", 'alias': 'bionic/amd64'},
'profiles': ['profilename'] }
To modify container's configuration method `save` should be called after
:class:`~container.Container` attributes changes.
>>> container = client.containers.get('my-container')
>>> container.ephemeral = False
>>> container.devices = { 'root': { 'path': '/', 'type': 'disk', 'size': '7GB'} }
>>> container.save
To get state information such as a network address.
.. code-block:: python
>>> addresses = container.state().network['eth0']['addresses']
>>> addresses[0]
{'family': 'inet', 'address': '10.251.77.182', 'netmask': '24', 'scope': 'global'}
To migrate a container between two servers, first you need to create a client certificate in order to connect to the remote server
openssl req -newkey rsa:2048 -nodes -keyout lxd.key -out lxd.csr
openssl x509 -signkey lxd.key -in lxd.csr -req -days 365 -out lxd.crt
Then you need to connect to both the destination server and the source server,
the source server has to be reachable by the destination server otherwise the migration will fail due to a websocket error
.. code-block:: python
from pylxd import Client
client_source=Client(endpoint='https://192.168.1.104:8443',cert=('lxd.crt','lxd.key'),verify=False)
client_destination=Client(endpoint='https://192.168.1.106:8443',cert=('lxd.crt','lxd.key'),verify=False)
cont = client_source.containers.get('testm')
cont.migrate(client_destination,wait=True)
This will migrate the container from source server to destination server
To migrate a live container, user the ``live=True`` parameter:
..code-block:: python
cont.migrate(client__destination, live=True, wait=True)
If you want an interactive shell in the container, you can attach to it via a websocket.
.. code-block:: python
>>> res = container.raw_interactive_execute(['/bin/bash'])
>>> res
{
"name": "container-name",
"ws": "/1.0/operations/adbaab82-afd2-450c-a67e-274726e875b1/websocket?secret=ef3dbdc103ec5c90fc6359c8e087dcaf1bc3eb46c76117289f34a8f949e08d87",
"control": "/1.0/operations/adbaab82-afd2-450c-a67e-274726e875b1/websocket?secret=dbbc67833009339d45140671773ac55b513e78b219f9f39609247a2d10458084"
}
You can connect to this urls from e.g. https://xtermjs.org/ .
Container Snapshots
-------------------
Each container carries its own manager for managing :class:`~container.Snapshot`
functionality. It has `get`, `all`, and `create` functionality.
Snapshots are keyed by their name (and only their name, in pylxd; LXD
keys them by <container-name>/<snapshot-name>, but the manager allows
us to use our own namespacing).
A container object (returned by `get` or `all`) has the following methods:
- `rename` - rename a snapshot
- `publish` - create an image from a snapshot. However, this may fail if the
image from the snapshot is bigger than the logical volume that is allocated
by lxc. See https://github.com/lxc/lxd/issues/2201 for more details. The solution
is to increase the `storage.lvm_volume_size` parameter in lxc.
- `restore` - restore the container to this snapshot.
.. code-block:: python
>>> snapshot = container.snapshots.get('an-snapshot')
>>> snapshot.created_at
'1983-06-16T2:38:00'
>>> snapshot.rename('backup-snapshot', wait=True)
>>> snapshot.delete(wait=True)
To create a new snapshot, use `create` with a `name` argument. If you want
to capture the contents of RAM in the snapshot, you can use `stateful=True`.
.. note:: Your LXD requires a relatively recent version of CRIU for this.
.. code-block:: python
>>> snapshot = container.snapshots.create(
... 'my-backup', stateful=True, wait=True)
>>> snapshot.name
'my-backup'
Container files
---------------
Containers also have a `files` manager for getting and putting files on the
container. The following methods are available on the `files` manager:
- `put` - push a file into the container.
- `get` - get a file from the container.
- `delete_available` - If the `file_delete` extension is available on the lxc
host, then this method returns `True` and the `delete` method is available.
- `delete` - delete a file on the container.
.. note:: All file operations use `uid` and `gid` of 0 in the container. i.e. root.
.. code-block:: python
>>> filedata = open('my-script').read()
>>> container.files.put('/tmp/my-script', filedata)
>>> newfiledata = container.files.get('/tmp/my-script2')
>>> open('my-script2', 'wb').write(newfiledata)
|