rockstar on Github | 26 Jun 02:06 2016

[pylxd/master] Fix the cert handling.

Attachment: text/x-mailbox, 835 bytes
From ba63defee76fb3a429076ea584ac4ba1e98c9a14 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 18:04:18 -0600
Subject: [PATCH] Fix the cert handling.

---
 pylxd/certificate.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/pylxd/certificate.py b/pylxd/certificate.py
index d116b63..5b395a1 100644
--- a/pylxd/certificate.py
+++ b/pylxd/certificate.py
 <at>  <at>  -50,9 +50,13  <at>  <at>  def all(cls, client):
     def create(cls, client, password, cert_data):
         """Create a new certificate."""
         cert = x509.load_pem_x509_certificate(cert_data, default_backend())
+        base64_cert = cert.public_bytes(Encoding.PEM).decode('utf-8')
+        if base64_cert.startswith('-----BEGIN CERTIFICATE-----'):
+            base64_cert = '\n'.join(
+                base64_cert.split('\n')[1:-2])
         data = {
             'type': 'client',
-            'certificate': cert.public_bytes(Encoding.PEM).decode('utf-8'),
+            'certificate': base64_cert,
             'password': password,
         }
         client.api.certificates.post(json=data)
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
rockstar on Github | 25 Jun 09:35 2016

[pylxd/master] Fix busybox.obfuscate (closes #143)

Attachment: text/x-mailbox, 410 bytes
From 1b8d72da39240c9a6e2d31b0bf88848388cdb38f Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 01:33:15 -0600
Subject: [PATCH] Fix busybox.obfuscate (closes #143)

---
 integration/busybox.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/integration/busybox.py b/integration/busybox.py
index 8e87b00..72bbca7 100644
--- a/integration/busybox.py
+++ b/integration/busybox.py
 <at>  <at>  -56,8 +56,7  <at>  <at>  def create_tarball(self, split=False):
                         'description': "Busybox %s" % os.uname()[4],
                         'name': "busybox-%s" % os.uname()[4],
                         # Don't overwrite actual busybox images.
-                        'obfuscate': str(uuid.uuid4),
-                        },
+                        'obfuscate': str(uuid.uuid4()), },
                     }

         # Add busybox
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
rockstar on Github | 25 Jun 09:31 2016

[pylxd/master] Stateful object experiment

Attachment: text/x-mailbox, 2521 bytes
From 2478240bdb5aaeaeae54452c84dcc1588ab2ebac Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Fri, 24 Jun 2016 21:12:23 -0600
Subject: [PATCH 1/5] Add coverage (why wasn't it already there?)

---
 test-requirements.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test-requirements.txt b/test-requirements.txt
index f76850a..0c5e340 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
 <at>  <at>  -2,5 +2,6  <at>  <at>  ddt>=0.7.0
 nose>=1.3.7
 mock>=1.3.0
 flake8>=2.5.0
+coverage>=4.1
 # See https://github.com/novafloss/mock-services/pull/15
 -e git://github.com/rockstar/mock-services.git <at> aba3977d1a3f43afd77d99f241ee1111c20deeed#egg=mock-services

From 4f824ac24f939476b51e8ef22d70867e17ba7e9b Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 00:01:13 -0600
Subject: [PATCH 2/5] Add the model.

---
 pylxd/model.py            | 126 ++++++++++++++++++++++++++++++++++++++++++++++
 pylxd/tests/test_model.py |  93 ++++++++++++++++++++++++++++++++++
 2 files changed, 219 insertions(+)
 create mode 100644 pylxd/model.py
 create mode 100644 pylxd/tests/test_model.py

diff --git a/pylxd/model.py b/pylxd/model.py
new file mode 100644
index 0000000..ad30a6d
--- /dev/null
+++ b/pylxd/model.py
 <at>  <at>  -0,0 +1,126  <at>  <at> 
+# Copyright (c) 2016 Canonical Ltd
+#
+#    Licensed 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 six
+
+
+class Attribute(object):
+    """A metadata class for model attributes."""
+
+    def __init__(self, validator=None):
+        self.validator = validator
+
+
+class ModelType(type):
+    """A Model metaclass.
+
+    This metaclass converts the declarative Attribute style
+    to attributes on the model instance itself.
+    """
+
+    def __new__(cls, name, bases, attrs):
+        if '__slots__' in attrs and name != 'Model':  # pragma: no cover
+            raise TypeError('__slots__ should not be specified.')
+        attributes = {}
+        for_removal = []
+
+        for key, val in attrs.items():
+            if type(val) == Attribute:
+                attributes[key] = val
+                for_removal.append(key)
+        for key in for_removal:
+            del attrs[key]
+
+        slots = list(attributes.keys())
+        if '__slots__' in attrs:
+            slots = slots + attrs['__slots__']
+        for base in bases:
+            if '__slots__' in dir(base):
+                slots = slots + base.__slots__
+        attrs['__slots__'] = slots
+        attrs['__attributes__'] = attributes
+
+        return super(ModelType, cls).__new__(cls, name, bases, attrs)
+
+
+ <at> six.add_metaclass(ModelType)
+class Model(object):
+    """A Base LXD object model.
+
+    Objects fetched from the LXD API have state, which allows
+    the objects to be used transactionally, with E-tag support,
+    and be smart about I/O.
+
+    The model lifecycle is this: A model's get/create methods will
+    return an instance. That instance may or may not be a partial
+    instance. If it is a partial instance, `sync` will be called
+    and the rest of the object retrieved from the server when
+    un-initialized attributes are read. When attributes are modified,
+    the instance is marked as dirty. `save` will save the changes
+    to the server.
+    """
+    __slots__ = ['client', '__dirty__']
+
+    def __init__(self, client, **kwargs):
+        self.client = client
+
+        for key, val in kwargs.items():
+            setattr(self, key, val)
+        self.__dirty__ = False
+
+    def __getattribute__(self, name):
+        try:
+            return super(Model, self).__getattribute__(name)
+        except AttributeError:
+            if name in self.__slots__:
+                self.sync()
+                return super(Model, self).__getattribute__(name)
+            else:
+                raise
+
+    def __setattr__(self, name, value):
+        if name in self.__attributes__:
+            attribute = self.__attributes__[name]
+
+            if attribute.validator is not None:
+                if attribute.validator is not type(value):
+                    value = attribute.validator(value)
+            self.__dirty__ = True
+        return super(Model, self).__setattr__(name, value)
+
+     <at> property
+    def dirty(self):
+        return self.__dirty__
+
+    def sync(self):  # pragma: no cover
+        """Sync from the server.
+
+        When collections of objects are retrieved from the server, they
+        are often partial objects. The full object must be retrieved before
+        it can modified. This method is called when getattr is called on
+        a non-initaliazed object.
+        """
+        raise NotImplemented('sync is not implemented')
+
+    def save(self):  # pragma: no cover
+        """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.
+        """
+        raise NotImplemented('save is not implemented')
+
+    def delete(self):  # pragma: no cover
+        """Delete an object from the server."""
+        raise NotImplemented('delete is not implemented')
diff --git a/pylxd/tests/test_model.py b/pylxd/tests/test_model.py
new file mode 100644
index 0000000..bfcb4b9
--- /dev/null
+++ b/pylxd/tests/test_model.py
 <at>  <at>  -0,0 +1,93  <at>  <at> 
+# Copyright (c) 2016 Canonical Ltd
+#
+#    Licensed 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.
+from pylxd import model
+from pylxd.tests import testing
+
+
+class Item(model.Model):
+    """A fake model."""
+    name = model.Attribute()
+    age = model.Attribute(int)
+    data = model.Attribute()
+
+    def sync(self):
+        self.age = 1000
+        self.data = {'key': 'val'}
+
+
+class TestModel(testing.PyLXDTestCase):
+    """Tests for pylxd.model.Model."""
+
+    def test_init(self):
+        """Initial attributes are set."""
+        item = Item(self.client, name='an-item', age=15, data={'key': 'val'})
+
+        self.assertEqual(self.client, item.client)
+        self.assertEqual('an-item', item.name)
+
+    def test_init_unknown_attribute(self):
+        """Unknown attributes raise an exception."""
+        self.assertRaises(
+            AttributeError,
+            Item, self.client, name='an-item', nonexistent='SRSLY')
+
+    def test_unknown_attribute(self):
+        """Setting unknown attributes raise an exception."""
+        def set_unknown_attribute():
+            item = Item(self.client, name='an-item')
+            item.nonexistent = 'SRSLY'
+        self.assertRaises(AttributeError, set_unknown_attribute)
+
+    def test_get_unknown_attribute(self):
+        """Setting unknown attributes raise an exception."""
+        def get_unknown_attribute():
+            item = Item(self.client, name='an-item')
+            return item.nonexistent
+        self.assertRaises(AttributeError, get_unknown_attribute)
+
+    def test_unset_attribute_sync(self):
+        """Reading unavailable attributes calls sync."""
+        item = Item(self.client, name='an-item')
+
+        self.assertEqual(1000, item.age)
+
+    def test_int_attribute_validator(self):
+        """Age is set properly to be an int."""
+        item = Item(self.client)
+
+        item.age = '100'
+
+        self.assertEqual(100, item.age)
+
+    def test_int_attribute_invalid(self):
+        """TypeError is raised when data can't be converted to type."""
+        def set_string():
+            item = Item(self.client)
+            item.age = 'abc'
+
+        self.assertRaises(ValueError, set_string)
+
+    def test_dirty(self):
+        """Changes mark the object as dirty."""
+        item = Item(self.client, name='an-item', age=15, data={'key': 'val'})
+
+        item.age = 100
+
+        self.assertTrue(item.dirty)
+
+    def test_not_dirty(self):
+        """Changes mark the object as dirty."""
+        item = Item(self.client, name='an-item', age=15, data={'key': 'val'})
+
+        self.assertFalse(item.dirty)

From 85cccb964180f8d0f3d51e46b9ccc1f6303d64b9 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 01:02:20 -0600
Subject: [PATCH 3/5] Add support for /1.0/networks

---
 pylxd/client.py             |  1 +
 pylxd/managers.py           |  8 ++++++--
 pylxd/model.py              |  6 +++++-
 pylxd/network.py            | 44 ++++++++++++++++++++++++++++++++++++++++++++
 pylxd/tests/mock_lxd.py     | 23 +++++++++++++++++++++++
 pylxd/tests/test_network.py | 33 +++++++++++++++++++++++++++++++++
 6 files changed, 112 insertions(+), 3 deletions(-)
 create mode 100644 pylxd/network.py
 create mode 100644 pylxd/tests/test_network.py

diff --git a/pylxd/client.py b/pylxd/client.py
index 5d66bfc..2890b43 100644
--- a/pylxd/client.py
+++ b/pylxd/client.py
 <at>  <at>  -194,6 +194,7  <at>  <at>  def __init__(self, endpoint=None, version='1.0', cert=None, verify=True):
         self.certificates = managers.CertificateManager(self)
         self.containers = managers.ContainerManager(self)
         self.images = managers.ImageManager(self)
+        self.networks = managers.NetworkManager(self)
         self.operations = managers.OperationManager(self)
         self.profiles = managers.ProfileManager(self)

diff --git a/pylxd/managers.py b/pylxd/managers.py
index ddb6214..b8c047e 100644
--- a/pylxd/managers.py
+++ b/pylxd/managers.py
 <at>  <at>  -37,13 +37,17  <at>  <at>  class ImageManager(BaseManager):
     manager_for = 'pylxd.image.Image'

 
-class ProfileManager(BaseManager):
-    manager_for = 'pylxd.profile.Profile'
+class NetworkManager(BaseManager):
+    manager_for = 'pylxd.network.Network'

 
 class OperationManager(BaseManager):
     manager_for = 'pylxd.operation.Operation'

 
+class ProfileManager(BaseManager):
+    manager_for = 'pylxd.profile.Profile'
+
+
 class SnapshotManager(BaseManager):
     manager_for = 'pylxd.container.Snapshot'
diff --git a/pylxd/model.py b/pylxd/model.py
index ad30a6d..d31e726 100644
--- a/pylxd/model.py
+++ b/pylxd/model.py
 <at>  <at>  -110,7 +110,11  <at>  <at>  def sync(self):  # pragma: no cover
         it can modified. This method is called when getattr is called on
         a non-initaliazed object.
         """
-        raise NotImplemented('sync is not implemented')
+        # XXX: rockstar (25 Jun 2016) - This has the potential to step
+        # on existing attributes.
+        response = self.api.get()
+        for key, val in response.json()['metadata'].items():
+            setattr(self, key, val)

     def save(self):  # pragma: no cover
         """Save data to the server.
diff --git a/pylxd/network.py b/pylxd/network.py
new file mode 100644
index 0000000..9609172
--- /dev/null
+++ b/pylxd/network.py
 <at>  <at>  -0,0 +1,44  <at>  <at> 
+# Copyright (c) 2016 Canonical Ltd
+#
+#    Licensed 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.
+from pylxd import model
+
+
+class Network(model.Model):
+    """A LXD network."""
+    name = model.Attribute()
+    type = model.Attribute()
+    used_by = model.Attribute()
+
+     <at> classmethod
+    def get(cls, client, name):
+        """Get a network by name."""
+        response = client.api.networks[name].get()
+
+        network = cls(client, **response.json()['metadata'])
+        return network
+
+     <at> classmethod
+    def all(cls, client):
+        """Get all networks."""
+        response = client.api.networks.get()
+
+        networks = []
+        for url in response.json()['metadata']:
+            name = url.split('/')[-1]
+            networks.append(cls(client, name=name))
+        return networks
+
+     <at> property
+    def api(self):
+        return self.client.api.networks[self.name]
diff --git a/pylxd/tests/mock_lxd.py b/pylxd/tests/mock_lxd.py
index 15ade82..8f5d531 100644
--- a/pylxd/tests/mock_lxd.py
+++ b/pylxd/tests/mock_lxd.py
 <at>  <at>  -335,6 +335,29  <at>  <at>  def profile_GET(request, context):
     },

 
+    # Networks
+    {
+        'json': {
+            'type': 'sync',
+            'metadata': [
+                'http://pylxd.test/1.0/networks/lo',
+            ]},
+        'method': 'GET',
+        'url': r'^http://pylxd.test/1.0/networks$',
+    },
+    {
+        'json': {
+            'type': 'sync',
+            'metadata': {
+                'name': 'lo',
+                'type': 'loopback',
+                'used_by': [],
+            }},
+        'method': 'GET',
+        'url': r'^http://pylxd.test/1.0/networks/lo$',
+    },
+
+
     # Profiles
     {
         'text': json.dumps({
diff --git a/pylxd/tests/test_network.py b/pylxd/tests/test_network.py
new file mode 100644
index 0000000..0aff49b
--- /dev/null
+++ b/pylxd/tests/test_network.py
 <at>  <at>  -0,0 +1,33  <at>  <at> 
+# Copyright (c) 2016 Canonical Ltd
+#
+#    Licensed 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.
+from pylxd import network
+from pylxd.tests import testing
+
+
+class TestNetwork(testing.PyLXDTestCase):
+    """Tests for pylxd.network.Network."""
+
+    def test_all(self):
+        """A list of all networks are returned."""
+        networks = network.Network.all(self.client)
+
+        self.assertEqual(1, len(networks))
+
+    def test_get(self):
+        """Return a container."""
+        name = 'lo'
+
+        an_network = network.Network.get(self.client, name)
+
+        self.assertEqual(name, an_network.name)

From d98910e41c720fe22cb19c2421f5f13e1009ab95 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 01:05:12 -0600
Subject: [PATCH 4/5] sync should be covered by test coverage

---
 pylxd/model.py              | 2 +-
 pylxd/tests/test_network.py | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/pylxd/model.py b/pylxd/model.py
index d31e726..c3ce6c0 100644
--- a/pylxd/model.py
+++ b/pylxd/model.py
 <at>  <at>  -102,7 +102,7  <at>  <at>  def __setattr__(self, name, value):
     def dirty(self):
         return self.__dirty__

-    def sync(self):  # pragma: no cover
+    def sync(self):
         """Sync from the server.

         When collections of objects are retrieved from the server, they
diff --git a/pylxd/tests/test_network.py b/pylxd/tests/test_network.py
index 0aff49b..3534c83 100644
--- a/pylxd/tests/test_network.py
+++ b/pylxd/tests/test_network.py
 <at>  <at>  -31,3 +31,9  <at>  <at>  def test_get(self):
         an_network = network.Network.get(self.client, name)

         self.assertEqual(name, an_network.name)
+
+    def test_partial(self):
+        """A partial network is synced."""
+        an_network = network.Network(self.client, name='lo')
+
+        self.assertEqual('loopback', an_network.type)

From 7e906f9d9ca33727fc6ef1809ca97acf04def509 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Sat, 25 Jun 2016 01:08:43 -0600
Subject: [PATCH 5/5] `delete` and `save` are not allowed in networks

---
 pylxd/tests/test_network.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/pylxd/tests/test_network.py b/pylxd/tests/test_network.py
index 3534c83..20215a6 100644
--- a/pylxd/tests/test_network.py
+++ b/pylxd/tests/test_network.py
 <at>  <at>  -37,3 +37,15  <at>  <at>  def test_partial(self):
         an_network = network.Network(self.client, name='lo')

         self.assertEqual('loopback', an_network.type)
+
+    def test_delete(self):
+        """delete is not implemented in networks."""
+        an_network = network.Network(self.client, name='lo')
+
+        self.assertRaises(NotImplemented, an_network.delete)
+
+    def test_save(self):
+        """save is not implemented in networks."""
+        an_network = network.Network(self.client, name='lo')
+
+        self.assertRaises(NotImplemented, an_network.save)
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
hallyn on Github | 25 Jun 07:10 2016

[lxc/master] container start: clone newcgroup immediately

Attachment: text/x-mailbox, 1577 bytes
From 9fac8fbbd9801867c5329ba43a9cbc0f0e80e14c Mon Sep 17 00:00:00 2001
From: Serge Hallyn <serge <at> hallyn.com>
Date: Sat, 25 Jun 2016 00:05:08 -0500
Subject: [PATCH] container start: clone newcgroup immediately

rather than waiting and later unsharing.

This "makes the creation of a new cgroup early enough that the existing
cgroup mounts are visible.  Which means any fancy permission checks
I dream will work on a future version of liblxc."

This also includes what should be a tiny improvement regarding netns,
though it's conceivable it'll break something.  Remember that with new
kernels we need to unshare netns after we've become the root user in the
new userns, so that netns files are owned by that root.  But we were
passing the unfiltered handler->clone_flags to the original clone().
This just resulted in a temporary extra netns generation, but still
worked since our target netns, which we passed our devices into, was
created late enough.

Signed-off-by: Serge Hallyn <serge <at> hallyn.com>
Signed-off-by: "Eric W. Biederman" <ebiederm <at> xmission.com>
---
 src/lxc/start.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/lxc/start.c b/src/lxc/start.c
index 5437206..b7f58cd 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
 <at>  <at>  -908,11 +908,6  <at>  <at>  static int do_start(void *data)
 		devnull_fd = -1;
 	}

-	if (cgns_supported() && unshare(CLONE_NEWCGROUP) != 0) {
-		SYSERROR("Failed to unshare cgroup namespace");
-		goto out_warn_father;
-	}
-
 	setsid();

 	/* after this call, we are in error because this
 <at>  <at>  -1135,7 +1130,11  <at>  <at>  static int lxc_spawn(struct lxc_handler *handler)
 	flags = handler->clone_flags;
 	if (handler->clone_flags & CLONE_NEWUSER)
 		flags &= ~CLONE_NEWNET;
-	handler->pid = lxc_clone(do_start, handler, handler->clone_flags);
+	if (cgns_supported()) {
+		handler->clone_flags |= CLONE_NEWCGROUP;
+		flags |= CLONE_NEWCGROUP;
+	}
+	handler->pid = lxc_clone(do_start, handler, flags);
 	if (handler->pid < 0) {
 		SYSERROR("failed to fork into a new namespace");
 		goto out_delete_net;
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
sean-jc on Github | 24 Jun 19:12 2016

[lxd/master] Add function to consolidate Do's error handling

Attachment: text/x-mailbox, 1095 bytes
From b08c8faca061b084b45bb185fc66448f012d6d0a Mon Sep 17 00:00:00 2001
From: Sean Christopherson <sean.j.christopherson <at> intel.com>
Date: Fri, 24 Jun 2016 09:43:54 -0700
Subject: [PATCH] Add function to consolidate Do's error handling

Add a function that can be called inline with return to handle all
cleanup if an error occurs after migration has been started.  This
fixes a missing call to driver.Cleanup() and hopefully reduces the
probability of a similar bug occuring in the future.

Signed-off-by: Sean Christopherson <sean.j.christopherson <at> intel.com>
---
 lxd/migrate.go | 38 ++++++++++++++++++--------------------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/lxd/migrate.go b/lxd/migrate.go
index 651b257..3ea1a76 100644
--- a/lxd/migrate.go
+++ b/lxd/migrate.go
 <at>  <at>  -302,37 +302,39  <at>  <at>  func (s *migrationSourceWs) Do(op *operation) error {
 		driver, _ = rsyncMigrationSource(s.container)
 	}

-	if err := driver.SendWhileRunning(s.fsConn); err != nil {
+	// All failure paths need to do a few things to correctly handle errors before returning.
+	// Unfortunately, handling errors is not well-suited to defer as the code depends on the
+	// status of driver and the error value.  The error value is especially tricky due to the
+	// common case of creating a new err variable (intentional or not) due to scoping and use
+	// of ":=".  Capturing err in a closure for use in defer would be fragile, which defeats
+	// the purpose of using defer.  An abort function reduces the odds of mishandling errors
+	// without introducing the fragility of closing on err.
+	abort := func(err error) error {
 		driver.Cleanup()
 		s.sendControl(err)
 		return err
 	}

+	if err := driver.SendWhileRunning(s.fsConn); err != nil {
+		return abort(err)
+	}
+
 	if s.live {
 		if header.Criu == nil {
-			driver.Cleanup()
-			err := fmt.Errorf("Got no CRIU socket type for live migration")
-			s.sendControl(err)
-			return err
+			return abort(fmt.Errorf("Got no CRIU socket type for live migration"))
 		} else if *header.Criu != CRIUType_CRIU_RSYNC {
-			driver.Cleanup()
-			err := fmt.Errorf("Formats other than criu rsync not understood")
-			s.sendControl(err)
-			return err
+			return abort(fmt.Errorf("Formats other than criu rsync not understood"))
 		}

 		checkpointDir, err := ioutil.TempDir("", "lxd_checkpoint_")
 		if err != nil {
-			driver.Cleanup()
-			s.sendControl(err)
-			return err
+			return abort(err)
 		}
 		defer os.RemoveAll(checkpointDir)

 		err = s.container.Migrate(lxc.MIGRATE_DUMP, checkpointDir, "migration", true)
 		if err != nil {
-			s.sendControl(err)
-			return err
+			return abort(err)
 		}

 		/*
 <at>  <at>  -343,15 +345,11  <at>  <at>  func (s *migrationSourceWs) Do(op *operation) error {
 		 * p.haul's protocol, it will make sense to do these in parallel.
 		 */
 		if err := RsyncSend(shared.AddSlash(checkpointDir), s.criuConn); err != nil {
-			driver.Cleanup()
-			s.sendControl(err)
-			return err
+			return abort(err)
 		}

 		if err := driver.SendAfterCheckpoint(s.fsConn); err != nil {
-			driver.Cleanup()
-			s.sendControl(err)
-			return err
+			return abort(err)
 		}
 	}

_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
sean-jc on Github | 24 Jun 17:29 2016

[lxd/master] Add support for config key columns in 'lxc list'

Attachment: text/x-mailbox, 953 bytes
From 448c8b0f10120e2e45b36e97740981c12331070b Mon Sep 17 00:00:00 2001
From: Sean Christopherson <sean.j.christopherson <at> intel.com>
Date: Thu, 23 Jun 2016 08:14:47 -0700
Subject: [PATCH] Add support for config key columns in 'lxc list'

Extend column option for table format to accept a separated list of
attributes to display.  Attributes can be specified using pre-defined
shorthand chars (existing functionality) or via config keys (new).

Move syntactic checking functionality into shared/container.go so
that LXC can perform basic validation of config keys.

Signed-off-by: Sean Christopherson <sean.j.christopherson <at> intel.com>
---
 lxc/list.go         | 120 +++++++++++++++---
 lxc/list_test.go    | 158 ++++++++++++++++++++++++
 lxd/container.go    | 123 ++-----------------
 po/de.po            | 305 ++++++++++++++++++++++++++--------------------
 po/fr.po            | 305 ++++++++++++++++++++++++++--------------------
 po/ja.po            | 345 ++++++++++++++++++++++++++++++----------------------
 po/lxd.pot          |  71 +++++++----
 shared/container.go | 116 ++++++++++++++++++
 shared/util.go      |  12 ++
 9 files changed, 983 insertions(+), 572 deletions(-)

diff --git a/lxc/list.go b/lxc/list.go
index 12afac8..6e2f9b7 100644
--- a/lxc/list.go
+++ b/lxc/list.go
 <at>  <at>  -6,6 +6,7  <at>  <at>  import (
 	"os"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 
 <at>  <at>  -54,9 +55,9  <at>  <at>  const (
 )
 
 type listCmd struct {
-	chosenColumnRunes string
-	fast              bool
-	format            string
+	columnsRaw string
+	fast       bool
+	format     string
 }
 
 func (c *listCmd) showByDefault() bool {
 <at>  <at>  -79,7 +80,13  <at>  <at>  The filters are:
  * "s.privileged=1" will do the same
 * A regular expression matching a configuration item or its value. (e.g. volatile.eth0.hwaddr=00:16:3e:.*)
 
-Columns for table format are:
+The -c option takes a comma separated list of arguments that control
+which container attributes to output when displaying in table format.
+Column arguments are either pre-defined shorthand chars (see below),
+or (extended) config keys.  Commas between consecutive shorthand chars
+are optional.
+
+Pre-defined shorthand chars:
 * 4 - IPv4 address
 * 6 - IPv6 address
 * a - architecture
 <at>  <at>  -92,13 +99,27  <at>  <at>  Columns for table format are:
 * S - number of snapshots
 * t - type (persistent or ephemeral)
 
+Config key syntax: key[:name][:maxWidth]
+* key      - The (extended) config key to display
+* name     - Name to display in the column header, defaults to the key
+             if not specified or if empty (to allow defining maxWidth
+             without a custom name, e.g. user.key::0)
+* maxWidth - Max width of the column (longer results are truncated).
+             -1 == unlimited
+              0 == width of column header
+             >0 == max width in chars
+             Default is -1 (unlimited)
+
 Default column layout: ns46tS
-Fast column layout: nsacPt`)
+Fast column layout: nsacPt
+
+Example: lxc list -c n,volatile.base_image:"BASE IMAGE":0,s46,volatile.eth0.hwaddr:MAC
+`)
 }
 
 func (c *listCmd) flags() {
-	gnuflag.StringVar(&c.chosenColumnRunes, "c", "ns46tS", i18n.G("Columns"))
-	gnuflag.StringVar(&c.chosenColumnRunes, "columns", "ns46tS", i18n.G("Columns"))
+	gnuflag.StringVar(&c.columnsRaw, "c", "ns46tS", i18n.G("Columns"))
+	gnuflag.StringVar(&c.columnsRaw, "columns", "ns46tS", i18n.G("Columns"))
 	gnuflag.StringVar(&c.format, "format", "table", i18n.G("Format"))
 	gnuflag.BoolVar(&c.fast, "fast", false, i18n.G("Fast mode (same as --columns=nsacPt"))
 }
 <at>  <at>  -373,7 +394,16  <at>  <at>  func (c *listCmd) run(config *lxd.Config, args []string) error {
 		cts = append(cts, cinfo)
 	}
 
-	columns_map := map[rune]column{
+	columns, err := c.parseColumns()
+	if err != nil {
+		return err
+	}
+
+	return c.listContainers(d, cts, filters, columns)
+}
+
+func (c *listCmd) parseColumns() ([]column, error) {
+	columnsShorthandMap := map[rune]column{
 		'4': column{i18n.G("IPV4"), c.IP4ColumnData, true, false},
 		'6': column{i18n.G("IPV6"), c.IP6ColumnData, true, false},
 		'a': column{i18n.G("ARCHITECTURE"), c.ArchitectureColumnData, false, false},
 <at>  <at>  -388,19 +418,79  <at>  <at>  func (c *listCmd) run(config *lxd.Config, args []string) error {
 	}
 
 	if c.fast {
-		c.chosenColumnRunes = "nsacPt"
+		c.columnsRaw = "nsacPt"
 	}
 
+	columnList := strings.Split(c.columnsRaw, ",")
+
 	columns := []column{}
-	for _, columnRune := range c.chosenColumnRunes {
-		if column, ok := columns_map[columnRune]; ok {
-			columns = append(columns, column)
+	for _, columnEntry := range columnList {
+		if columnEntry == "" {
+			return nil, fmt.Errorf("Empty column entry (redundant, leading or trailing command) in '%s'", c.columnsRaw)
+		}
+
+		// Config keys always contain a period, parse anything without a
+		// period as a series of shorthand runes.
+		if !strings.Contains(columnEntry, ".") {
+			for _, columnRune := range columnEntry {
+				if column, ok := columnsShorthandMap[columnRune]; ok {
+					columns = append(columns, column)
+				} else {
+					return nil, fmt.Errorf("Unknown column shorthand char '%c' in '%s'", columnRune, columnEntry)
+				}
+			}
 		} else {
-			return fmt.Errorf("%s does contain invalid column characters\n", c.chosenColumnRunes)
+			cc := strings.Split(columnEntry, ":")
+			if len(cc) > 3 {
+				return nil, fmt.Errorf("Invalid config key column format (too many fields): '%s'", columnEntry)
+			}
+
+			k := cc[0]
+			if _, err := shared.ConfigKeyChecker(k); err != nil {
+				return nil, fmt.Errorf("Invalid config key '%s' in '%s'", k, columnEntry)
+			}
+
+			column := column{Name: k}
+			if len(cc) > 1 {
+				if len(cc[1]) == 0 && len(cc) != 3 {
+					return nil, fmt.Errorf("Invalid name in '%s', empty string is only allowed when defining maxWidth", columnEntry)
+				}
+				column.Name = cc[1]
+			}
+
+			maxWidth := -1
+			if len(cc) > 2 {
+				temp, err := strconv.ParseInt(cc[2], 10, 64)
+				if err != nil {
+					return nil, fmt.Errorf("Invalid max width (must be an integer) '%s' in '%s'", cc[2], columnEntry)
+				}
+				if temp < -1 {
+					return nil, fmt.Errorf("Invalid max width (must -1, 0 or a positive integer) '%s' in '%s'", cc[2], columnEntry)
+				}
+				if temp == 0 {
+					maxWidth = len(column.Name)
+				} else {
+					maxWidth = int(temp)
+				}
+			}
+
+			column.Data = func(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+				v, ok := cInfo.Config[k]
+				if !ok {
+					v, ok = cInfo.ExpandedConfig[k]
+				}
+
+				// Truncate the data according to the max width.  A negative max width
+				// indicates there is no effective limit.
+				if maxWidth > 0 && len(v) > maxWidth {
+					return v[:maxWidth]
+				}
+				return v
+			}
+			columns = append(columns, column)
 		}
 	}
-
-	return c.listContainers(d, cts, filters, columns)
+	return columns, nil
 }
 
 func (c *listCmd) nameColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
diff --git a/lxc/list_test.go b/lxc/list_test.go
index 47e8135..ae4fe0f 100644
--- a/lxc/list_test.go
+++ b/lxc/list_test.go
 <at>  <at>  -1,6 +1,10  <at>  <at> 
 package main
 
 import (
+	"bytes"
+	"math/rand"
+	"strconv"
+	"strings"
 	"testing"
 
 	"github.com/lxc/lxd/shared"
 <at>  <at>  -45,3 +49,157  <at>  <at>  func TestShouldShow(t *testing.T) {
 		t.Errorf("value filter didn't work")
 	}
 }
+
+// Used by TestColumns and TestInvalidColumns
+const shorthand = "46aclnpPsSt"
+const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+func TestColumns(t *testing.T) {
+	keys := make([]string, 0, len(shared.KnownContainerConfigKeys))
+	for k := range shared.KnownContainerConfigKeys {
+		keys = append(keys, k)
+	}
+
+	randShorthand := func(buffer *bytes.Buffer) {
+		buffer.WriteByte(shorthand[rand.Intn(len(shorthand))])
+	}
+
+	randString := func(buffer *bytes.Buffer) {
+		l := rand.Intn(20)
+		if l == 0 {
+			l = rand.Intn(20) + 20
+		}
+		for i := 0; i < l; i++ {
+			buffer.WriteByte(alphanum[rand.Intn(len(alphanum))])
+		}
+	}
+
+	randConfigKey := func(buffer *bytes.Buffer) {
+		// Unconditionally prepend a comma so that we don't create an invalid
+		// column string, redundant commas will be handled immediately prior
+		// to parsing the string.
+		buffer.WriteRune(',')
+
+		switch rand.Intn(4) {
+		case 0:
+			buffer.WriteString(keys[rand.Intn(len(keys))])
+		case 1:
+			buffer.WriteString("user.")
+			randString(buffer)
+		case 2:
+			buffer.WriteString("environment.")
+			randString(buffer)
+		case 3:
+			if rand.Intn(2) == 0 {
+				buffer.WriteString("volatile.")
+				randString(buffer)
+				buffer.WriteString(".hwaddr")
+			} else {
+				buffer.WriteString("volatile.")
+				randString(buffer)
+				buffer.WriteString(".name")
+			}
+		}
+
+		// Randomize the optional fields in a single shot.  Empty names are legal
+		// when specifying the max width, append an extra colon in this case.
+		opt := rand.Intn(8)
+		if opt&1 != 0 {
+			buffer.WriteString(":")
+			randString(buffer)
+		} else if opt != 0 {
+			buffer.WriteString(":")
+		}
+
+		switch opt {
+		case 2, 3:
+			buffer.WriteString(":-1")
+		case 4, 5:
+			buffer.WriteString(":0")
+		case 6, 7:
+			buffer.WriteRune(':')
+			buffer.WriteString(strconv.FormatUint(uint64(rand.Uint32()), 10))
+		}
+
+		// Unconditionally append a comma so that we don't create an invalid
+		// column string, redundant commas will be handled immediately prior
+		// to parsing the string.
+		buffer.WriteRune(',')
+	}
+
+	for i := 0; i < 1000; i++ {
+		go func() {
+			var buffer bytes.Buffer
+
+			l := rand.Intn(10)
+			if l == 0 {
+				l = rand.Intn(10) + 10
+			}
+
+			num := l
+			for j := 0; j < l; j++ {
+				switch rand.Intn(5) {
+				case 0:
+					if buffer.Len() > 0 {
+						buffer.WriteRune(',')
+						num--
+					} else {
+						randShorthand(&buffer)
+					}
+
+				case 1, 2:
+					randShorthand(&buffer)
+				case 3, 4:
+					randConfigKey(&buffer)
+				}
+			}
+
+			// Generate the column string, removing any leading, trailing or duplicate commas.
+			raw := shared.RemoveDuplicatesFromString(strings.Trim(buffer.String(), ","), ",")
+
+			list := listCmd{columnsRaw: raw}
+
+			columns, err := list.parseColumns()
+			if err != nil {
+				t.Errorf("Failed to parse columns string.  Input: %s, Error: %s", raw, err)
+			}
+			if len(columns) != num {
+				t.Errorf("Did not generate correct number of columns.  Expected: %d, Actual: %d, Input: %s", num, len(columns), raw)
+			}
+		}()
+	}
+}
+
+func TestInvalidColumns(t *testing.T) {
+	run := func(raw string) {
+		list := listCmd{columnsRaw: raw}
+		_, err := list.parseColumns()
+		if err == nil {
+			t.Errorf("Expected error from parseColumns, received nil.  Input: %s", raw)
+		}
+	}
+
+	for _, v := range alphanum {
+		if !strings.ContainsRune(shorthand, v) {
+			run(string(v))
+		}
+	}
+
+	run(",")
+	run(",a")
+	run("a,")
+	run("4,,6")
+	run(".")
+	run(":")
+	run("::")
+	run(".key:")
+	run("user.key:")
+	run("user.key::")
+	run(":user.key")
+	run(":user.key:0")
+	run("user.key::-2")
+	run("user.key:name:-2")
+	run("volatile")
+	run("base_image")
+	run("volatile.image")
+}
diff --git a/lxd/container.go b/lxd/container.go
index df19988..dd1038c 100644
--- a/lxd/container.go
+++ b/lxd/container.go
 <at>  <at>  -4,7 +4,6  <at>  <at>  import (
 	"fmt"
 	"io"
 	"os"
-	"strconv"
 	"strings"
 	"time"
 
 <at>  <at>  -37,84 +36,17  <at>  <at>  func containerValidName(name string) error {
 }
 
 func containerValidConfigKey(d *Daemon, key string, value string) error {
-	isInt64 := func(key string, value string) error {
-		if value == "" {
-			return nil
-		}
-
-		_, err := strconv.ParseInt(value, 10, 64)
-		if err != nil {
-			return fmt.Errorf("Invalid value for an integer: %s", value)
-		}
-
-		return nil
+	f, err := shared.ConfigKeyChecker(key)
+	if err != nil {
+		return err
 	}
-
-	isBool := func(key string, value string) error {
-		if value == "" {
-			return nil
-		}
-
-		if !shared.StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
-			return fmt.Errorf("Invalid value for a boolean: %s", value)
-		}
-
-		return nil
+	if err = f(value); err != nil {
+		return err
 	}
-
-	isOneOf := func(key string, value string, valid []string) error {
-		if value == "" {
-			return nil
-		}
-
-		if !shared.StringInSlice(value, valid) {
-			return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
-		}
-
-		return nil
+	if key == "raw.lxc" {
+		return lxcValidConfig(value)
 	}
-
-	switch key {
-	case "boot.autostart":
-		return isBool(key, value)
-	case "boot.autostart.delay":
-		return isInt64(key, value)
-	case "boot.autostart.priority":
-		return isInt64(key, value)
-	case "boot.host_shutdown_timeout":
-		return isInt64(key, value)
-	case "limits.cpu":
-		return nil
-	case "limits.cpu.allowance":
-		return nil
-	case "limits.cpu.priority":
-		return isInt64(key, value)
-	case "limits.disk.priority":
-		return isInt64(key, value)
-	case "limits.memory":
-		return nil
-	case "limits.memory.enforce":
-		return isOneOf(key, value, []string{"soft", "hard"})
-	case "limits.memory.swap":
-		return isBool(key, value)
-	case "limits.memory.swap.priority":
-		return isInt64(key, value)
-	case "limits.network.priority":
-		return isInt64(key, value)
-	case "limits.processes":
-		return isInt64(key, value)
-	case "linux.kernel_modules":
-		return nil
-	case "security.privileged":
-		return isBool(key, value)
-	case "security.nesting":
-		return isBool(key, value)
-	case "security.syscalls.blacklist_default":
-		return isBool(key, value)
-	case "security.syscalls.blacklist_compat":
-		if err := isBool(key, value); err != nil {
-			return err
-		}
+	if key == "security.syscalls.blacklist_compat" {
 		for _, arch := range d.architectures {
 			if arch == shared.ARCH_64BIT_INTEL_X86 ||
 				arch == shared.ARCH_64BIT_ARMV8_LITTLE_ENDIAN ||
 <at>  <at>  -123,45 +55,8  <at>  <at>  func containerValidConfigKey(d *Daemon, key string, value string) error {
 			}
 		}
 		return fmt.Errorf("security.syscalls.blacklist_compat is only valid on x86_64")
-	case "security.syscalls.blacklist":
-		return nil
-	case "security.syscalls.whitelist":
-		return nil
-	case "raw.apparmor":
-		return nil
-	case "raw.lxc":
-		return lxcValidConfig(value)
-	case "raw.seccomp":
-		return nil
-	case "volatile.apply_template":
-		return nil
-	case "volatile.base_image":
-		return nil
-	case "volatile.last_state.idmap":
-		return nil
-	case "volatile.last_state.power":
-		return nil
-	}
-
-	if strings.HasPrefix(key, "volatile.") {
-		if strings.HasSuffix(key, ".hwaddr") {
-			return nil
-		}
-
-		if strings.HasSuffix(key, ".name") {
-			return nil
-		}
-	}
-
-	if strings.HasPrefix(key, "environment.") {
-		return nil
-	}
-
-	if strings.HasPrefix(key, "user.") {
-		return nil
 	}
-
-	return fmt.Errorf("Bad key: %s", key)
+	return nil
 }
 
 func containerValidDeviceConfigKey(t, k string) bool {
diff --git a/po/de.po b/po/de.po
index 81b8e11..40ab65f 100644
--- a/po/de.po
+++ b/po/de.po
 <at>  <at>  -7,7 +7,7  <at>  <at>  msgid ""
 msgstr ""
 "Project-Id-Version: LXD\n"
 "Report-Msgid-Bugs-To: lxc-devel <at> lists.linuxcontainers.org\n"
-"POT-Creation-Date: 2016-04-25 14:47-0500\n"
+"POT-Creation-Date: 2016-06-24 08:22-0700\n"
 "PO-Revision-Date: 2015-06-13 06:10+0200\n"
 "Last-Translator: Felix Engelmann <felix-lxd <at> nlogn.org>\n"
 "Language-Team: \n"
 <at>  <at>  -67,7 +67,7  <at>  <at>  msgstr ""
 "###\n"
 "### Der Name wird zwar angezeigt, lässt sich jedoch nicht ändern.\n"
 
-#: lxc/image.go:83
+#: lxc/image.go:85
 #, fuzzy
 msgid ""
 "### This is a yaml representation of the image properties.\n"
 <at>  <at>  -123,7 +123,7  <at>  <at>  msgstr ""
 "###\n"
 "### Der Name wird zwar angezeigt, lässt sich jedoch nicht ändern.\n"
 
-#: lxc/image.go:583
+#: lxc/image.go:603
 #, c-format
 msgid "%s (%d more)"
 msgstr ""
 <at>  <at>  -133,19 +133,19  <at>  <at>  msgstr ""
 msgid "'/' not allowed in snapshot name"
 msgstr "'/' ist kein gültiges Zeichen im Namen eines Sicherungspunktes\n"
 
-#: lxc/profile.go:251
+#: lxc/profile.go:253
 msgid "(none)"
 msgstr ""
 
-#: lxc/image.go:604 lxc/image.go:633
+#: lxc/image.go:624 lxc/image.go:666
 msgid "ALIAS"
 msgstr ""
 
-#: lxc/image.go:608
+#: lxc/image.go:628
 msgid "ARCH"
 msgstr ""
 
-#: lxc/list.go:378
+#: lxc/list.go:409
 msgid "ARCHITECTURE"
 msgstr ""
 
 <at>  <at>  -153,12 +153,12  <at>  <at>  msgstr ""
 msgid "Accept certificate"
 msgstr "Akzeptiere Zertifikat"
 
-#: lxc/remote.go:256
+#: lxc/remote.go:269
 #, c-format
 msgid "Admin password for %s: "
 msgstr "Administrator Passwort für %s: "
 
-#: lxc/image.go:347
+#: lxc/image.go:356
 #, fuzzy
 msgid "Aliases:"
 msgstr "Aliasse:\n"
 <at>  <at>  -167,12 +167,12  <at>  <at>  msgstr "Aliasse:\n"
 msgid "An environment variable of the form HOME=/home/foo"
 msgstr ""
 
-#: lxc/image.go:330 lxc/info.go:90
+#: lxc/image.go:339 lxc/info.go:90
 #, fuzzy, c-format
 msgid "Architecture: %s"
 msgstr "Architektur: %s\n"
 
-#: lxc/image.go:351
+#: lxc/image.go:360
 #, c-format
 msgid "Auto update: %s"
 msgstr ""
 <at>  <at>  -189,11 +189,11  <at>  <at>  msgstr ""
 msgid "Bytes sent"
 msgstr ""
 
-#: lxc/config.go:270
+#: lxc/config.go:274
 msgid "COMMON NAME"
 msgstr ""
 
-#: lxc/list.go:379
+#: lxc/list.go:410
 msgid "CREATED AT"
 msgstr ""
 
 <at>  <at>  -207,11 +207,11  <at>  <at>  msgstr ""
 msgid "Can't unset key '%s', it's not currently set."
 msgstr ""
 
-#: lxc/profile.go:417
+#: lxc/profile.go:419
 msgid "Cannot provide container name to list"
 msgstr ""
 
-#: lxc/remote.go:206
+#: lxc/remote.go:219
 #, fuzzy, c-format
 msgid "Certificate fingerprint: %x"
 msgstr "Fingerabdruck des Zertifikats: % x\n"
 <at>  <at>  -227,11 +227,11  <at>  <at>  msgstr ""
 "\n"
 "lxd %s <Name>\n"
 
-#: lxc/remote.go:279
+#: lxc/remote.go:292
 msgid "Client certificate stored at server: "
 msgstr "Gespeichertes Nutzerzertifikat auf dem Server: "
 
-#: lxc/list.go:99 lxc/list.go:100
+#: lxc/list.go:121 lxc/list.go:122
 msgid "Columns"
 msgstr ""
 
 <at>  <at>  -240,12 +240,12  <at>  <at>  msgstr ""
 msgid "Config key/value to apply to the new container"
 msgstr "kann nicht zum selben Container Namen kopieren"
 
-#: lxc/config.go:500 lxc/config.go:565 lxc/image.go:687 lxc/profile.go:215
+#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:720 lxc/profile.go:217
 #, fuzzy, c-format
 msgid "Config parsing error: %s"
 msgstr "YAML Analyse Fehler %v\n"
 
-#: lxc/main.go:37
+#: lxc/main.go:29
 msgid "Connection refused; is LXD running?"
 msgstr ""
 
 <at>  <at>  -263,7 +263,7  <at>  <at>  msgstr ""
 msgid "Container published with fingerprint: %s"
 msgstr "Abbild mit Fingerabdruck %s importiert\n"
 
-#: lxc/image.go:155
+#: lxc/image.go:157
 msgid "Copy aliases from source"
 msgstr "Kopiere Aliasse von der Quelle"
 
 <at>  <at>  -279,12 +279,12  <at>  <at>  msgstr ""
 "\n"
 "lxc copy <Quelle> <Ziel>\n"
 
-#: lxc/image.go:268
+#: lxc/image.go:271
 #, c-format
 msgid "Copying the image: %s"
 msgstr ""
 
-#: lxc/remote.go:221
+#: lxc/remote.go:234
 msgid "Could not create server cert dir"
 msgstr "Kann Verzeichnis für Zertifikate auf dem Server nicht erstellen"
 
 <at>  <at>  -308,7 +308,7  <at>  <at>  msgid ""
 "lxc snapshot u1 snap0"
 msgstr ""
 
-#: lxc/image.go:335 lxc/info.go:92
+#: lxc/image.go:344 lxc/info.go:92
 #, c-format
 msgid "Created: %s"
 msgstr ""
 <at>  <at>  -323,7 +323,7  <at>  <at>  msgstr ""
 msgid "Creating the container"
 msgstr "kann nicht zum selben Container Namen kopieren"
 
-#: lxc/image.go:607 lxc/image.go:635
+#: lxc/image.go:627 lxc/image.go:668
 msgid "DESCRIPTION"
 msgstr ""
 
 <at>  <at>  -343,29 +343,29  <at>  <at>  msgstr ""
 "Entfernt einen Container (oder Sicherungspunkt) und alle dazugehörigen\n"
 "Daten (Konfiguration, Sicherungspunkte, ...).\n"
 
-#: lxc/config.go:617
+#: lxc/config.go:648
 #, fuzzy, c-format
 msgid "Device %s added to %s"
 msgstr "Gerät %s wurde zu %s hinzugefügt\n"
 
-#: lxc/config.go:804
+#: lxc/config.go:835
 #, fuzzy, c-format
 msgid "Device %s removed from %s"
 msgstr "Gerät %s wurde von %s entfernt\n"
 
-#: lxc/list.go:462
+#: lxc/list.go:554
 msgid "EPHEMERAL"
 msgstr ""
 
-#: lxc/config.go:272
+#: lxc/config.go:276
 msgid "EXPIRY DATE"
 msgstr ""
 
-#: lxc/main.go:55
+#: lxc/main.go:41
 msgid "Enables debug mode."
 msgstr "Aktiviert Debug Modus"
 
-#: lxc/main.go:54
+#: lxc/main.go:40
 msgid "Enables verbose mode."
 msgstr "Aktiviert ausführliche Ausgabe"
 
 <at>  <at>  -397,24 +397,24  <at>  <at>  msgstr ""
 "\n"
 "lxc exec <Container> [--env EDITOR=/usr/bin/vim]... <Befehl>\n"
 
-#: lxc/image.go:339
+#: lxc/image.go:348
 #, c-format
 msgid "Expires: %s"
 msgstr ""
 
-#: lxc/image.go:341
+#: lxc/image.go:350
 msgid "Expires: never"
 msgstr ""
 
-#: lxc/config.go:269 lxc/image.go:605 lxc/image.go:634
+#: lxc/config.go:273 lxc/image.go:625 lxc/image.go:667
 msgid "FINGERPRINT"
 msgstr ""
 
-#: lxc/list.go:102
+#: lxc/list.go:124
 msgid "Fast mode (same as --columns=nsacPt"
 msgstr ""
 
-#: lxc/image.go:328
+#: lxc/image.go:337
 #, fuzzy, c-format
 msgid "Fingerprint: %s"
 msgstr "Fingerabdruck: %s\n"
 <at>  <at>  -430,7 +430,7  <at>  <at>  msgstr ""
 "\n"
 "lxc finger <remote>\n"
 
-#: lxc/action.go:37
+#: lxc/action.go:37 lxc/action.go:38
 msgid "Force the container to shutdown."
 msgstr "Herunterfahren des Containers erzwingen."
 
 <at>  <at>  -438,50 +438,50  <at>  <at>  msgstr "Herunterfahren des Containers erzwingen."
 msgid "Force the removal of stopped containers."
 msgstr ""
 
-#: lxc/main.go:56
+#: lxc/main.go:42
 msgid "Force using the local unix socket."
 msgstr ""
 
-#: lxc/list.go:101
+#: lxc/image.go:160 lxc/list.go:123
 msgid "Format"
 msgstr ""
 
-#: lxc/main.go:138
+#: lxc/remote.go:67
 #, fuzzy
 msgid "Generating a client certificate. This may take a minute..."
 msgstr "Generiere Nutzerzertifikat. Dies kann wenige Minuten dauern...\n"
 
-#: lxc/list.go:376
+#: lxc/list.go:407
 msgid "IPV4"
 msgstr ""
 
-#: lxc/list.go:377
+#: lxc/list.go:408
 msgid "IPV6"
 msgstr ""
 
-#: lxc/config.go:271
+#: lxc/config.go:275
 msgid "ISSUE DATE"
 msgstr ""
 
-#: lxc/main.go:146
+#: lxc/main.go:132
 msgid ""
 "If this is your first time using LXD, you should also run: sudo lxd init"
 msgstr ""
 
-#: lxc/main.go:57
+#: lxc/main.go:43
 msgid "Ignore aliases when determining what command to run."
 msgstr ""
 
-#: lxc/action.go:40
+#: lxc/action.go:41
 #, fuzzy
 msgid "Ignore the container state (only for start)."
 msgstr "Herunterfahren des Containers erzwingen."
 
-#: lxc/image.go:273
+#: lxc/image.go:276
 msgid "Image copied successfully!"
 msgstr ""
 
-#: lxc/image.go:419
+#: lxc/image.go:428
 #, fuzzy, c-format
 msgid "Image imported with fingerprint: %s"
 msgstr "Abbild mit Fingerabdruck %s importiert\n"
 <at>  <at>  -514,16 +514,21  <at>  <at>  msgstr ""
 "Beispiel:\n"
 "lxc launch ubuntu u1\n"
 
-#: lxc/remote.go:122
+#: lxc/remote.go:137
 #, c-format
 msgid "Invalid URL scheme \"%s\" in \"%s\""
 msgstr ""
 
+#: lxc/config.go:254
+#, fuzzy
+msgid "Invalid certificate"
+msgstr "Akzeptiere Zertifikat"
+
 #: lxc/init.go:30 lxc/init.go:35
 msgid "Invalid configuration key"
 msgstr ""
 
-#: lxc/file.go:190
+#: lxc/file.go:195
 #, c-format
 msgid "Invalid source %s"
 msgstr "Ungültige Quelle %s"
 <at>  <at>  -537,11 +542,15  <at>  <at>  msgstr "Ungültiges Ziel %s"
 msgid "Ips:"
 msgstr ""
 
-#: lxc/image.go:156
+#: lxc/image.go:158
 msgid "Keep the image up to date after initial copy"
 msgstr ""
 
-#: lxc/main.go:35
+#: lxc/list.go:411
+msgid "LAST USED AT"
+msgstr ""
+
+#: lxc/main.go:27
 msgid "LXD socket not found; is LXD installed and running?"
 msgstr ""
 
 <at>  <at>  -591,7 +600,7  <at>  <at>  msgstr ""
 "\n"
 "lxc info [<remote>:]Container [--show-log]\n"
 
-#: lxc/list.go:67
+#: lxc/list.go:68
 #, fuzzy
 msgid ""
 "Lists the available resources.\n"
 <at>  <at>  -612,11 +621,18  <at>  <at>  msgid ""
 "* A regular expression matching a configuration item or its value. (e.g. "
 "volatile.eth0.hwaddr=00:16:3e:.*)\n"
 "\n"
-"Columns for table format are:\n"
+"The -c option takes a comma separated list of arguments that control\n"
+"which container attributes to output when displaying in table format.\n"
+"Column arguments are either pre-defined shorthand chars (see below),\n"
+"or (extended) config keys.  Commas between consecutive shorthand chars\n"
+"are optional.\n"
+"\n"
+"Pre-defined shorthand chars:\n"
 "* 4 - IPv4 address\n"
 "* 6 - IPv6 address\n"
 "* a - architecture\n"
 "* c - creation date\n"
+"* l - last used date\n"
 "* n - name\n"
 "* p - pid of container init process\n"
 "* P - profiles\n"
 <at>  <at>  -624,8 +640,22  <at>  <at>  msgid ""
 "* S - number of snapshots\n"
 "* t - type (persistent or ephemeral)\n"
 "\n"
+"Config key syntax: key[:name][:maxWidth]\n"
+"* key      - The (extended) config key to display\n"
+"* name     - Name to display in the column header, defaults to the key\n"
+"             if not specified or if empty (to allow defining maxWidth\n"
+"             without a custom name, e.g. user.key::0)\n"
+"* maxWidth - Max width of the column (longer results are truncated).\n"
+"             -1 == unlimited\n"
+"              0 == width of column header\n"
+"             >0 == max width in chars\n"
+"             Default is -1 (unlimited)\n"
+"\n"
 "Default column layout: ns46tS\n"
-"Fast column layout: nsacPt"
+"Fast column layout: nsacPt\n"
+"\n"
+"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile."
+"eth0.hwaddr:MAC\n"
 msgstr ""
 "Listet vorhandene Ressourcen.\n"
 "\n"
 <at>  <at>  -646,7 +676,7  <at>  <at>  msgstr ""
 msgid "Log:"
 msgstr ""
 
-#: lxc/image.go:154
+#: lxc/image.go:156
 msgid "Make image public"
 msgstr "Veröffentliche Abbild"
 
 <at>  <at>  -674,16 +704,21  <at>  <at>  msgid ""
 "             cat profile.yml | lxc profile edit <profile> # read from "
 "profile.yml\n"
 "\n"
-"lxc profile apply <container> <profiles>\n"
-"    Apply a comma-separated list of profiles to a container, in order.\n"
+"lxc profile assign <container> <profiles>\n"
+"    Assign a comma-separated list of profiles to a container, in order.\n"
 "    All profiles passed in this call (and only those) will be applied\n"
-"    to the specified container.\n"
-"    Example: lxc profile apply foo default,bar # Apply default and bar\n"
-"             lxc profile apply foo default # Only default is active\n"
-"             lxc profile apply '' # no profiles are applied anymore\n"
-"             lxc profile apply bar,default # Apply default second now\n"
-"lxc profile apply-add <container> <profile>\n"
-"lxc profile apply-remove <container> <profile>\n"
+"    to the specified container, i.e. it sets the list of profiles exactly "
+"to\n"
+"    those specified in this command. To add/remove a particular profile from "
+"a\n"
+"    container, use {add|remove} below.\n"
+"    Example: lxc profile assign foo default,bar # Apply default and bar\n"
+"             lxc profile assign foo default # Only default is active\n"
+"             lxc profile assign '' # no profiles are applied anymore\n"
+"             lxc profile assign bar,default # Apply default second now\n"
+"lxc profile add <container> <profile> # add a profile to a container\n"
+"lxc profile remove <container> <profile> # remove the profile from a "
+"container\n"
 "\n"
 "Devices:\n"
 "lxc profile device list <profile>                                   List "
 <at>  <at>  -889,7 +924,7  <at>  <at>  msgstr ""
 "lxc remote get-default                                                    "
 "Gibt die Standard Instanz aus.\n"
 
-#: lxc/image.go:93
+#: lxc/image.go:95
 msgid ""
 "Manipulate container images.\n"
 "\n"
 <at>  <at>  -920,8 +955,8  <at>  <at>  msgid ""
 "    The auto-update flag instructs the server to keep this image up to\n"
 "    date. It requires the source to be an alias and for it to be public.\n"
 "\n"
-"lxc image delete [remote:]<image>\n"
-"    Delete an image from the LXD image store.\n"
+"lxc image delete [remote:]<image> [remote:][<image>...]\n"
+"    Delete one or more images from the LXD image store.\n"
 "\n"
 "lxc image export [remote:]<image>\n"
 "    Export an image from the LXD image store into a distributable tarball.\n"
 <at>  <at>  -929,7 +964,7  <at>  <at>  msgid ""
 "lxc image info [remote:]<image>\n"
 "    Print everything LXD knows about a given image.\n"
 "\n"
-"lxc image list [remote:] [filter]\n"
+"lxc image list [remote:] [filter] [--format table|json]\n"
 "    List images in the LXD image store. Filters may be of the\n"
 "    <key>=<value> form for property based filtering, or part of the image\n"
 "    hash or part of the image alias name.\n"
 <at>  <at>  -980,7 +1015,7  <at>  <at>  msgid ""
 "lxc monitor --type=logging"
 msgstr ""
 
-#: lxc/file.go:178
+#: lxc/file.go:183
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 "Mehr als eine Datei herunterzuladen, aber das Ziel ist kein Verzeichnis"
 <at>  <at>  -1001,16 +1036,16  <at>  <at>  msgstr ""
 "\n"
 "lxc move <Quelle> <Ziel>\n"
 
-#: lxc/action.go:63
+#: lxc/action.go:64
 #, fuzzy
 msgid "Must supply container name for: "
 msgstr "der Name des Ursprung Containers muss angegeben werden"
 
-#: lxc/list.go:380 lxc/remote.go:363
+#: lxc/list.go:412 lxc/remote.go:376
 msgid "NAME"
 msgstr ""
 
-#: lxc/remote.go:337 lxc/remote.go:342
+#: lxc/remote.go:350 lxc/remote.go:355
 msgid "NO"
 msgstr ""
 
 <at>  <at>  -1019,32 +1054,32  <at>  <at>  msgstr ""
 msgid "Name: %s"
 msgstr ""
 
-#: lxc/image.go:157 lxc/publish.go:33
+#: lxc/image.go:159 lxc/publish.go:33
 msgid "New alias to define at target"
 msgstr ""
 
-#: lxc/config.go:281
+#: lxc/config.go:285
 #, fuzzy
 msgid "No certificate provided to add"
 msgstr "Kein Zertifikat zum hinzufügen bereitgestellt"
 
-#: lxc/config.go:304
+#: lxc/config.go:308
 msgid "No fingerprint specified."
 msgstr "Kein Fingerabdruck angegeben."
 
-#: lxc/remote.go:107
+#: lxc/remote.go:122
 msgid "Only https URLs are supported for simplestreams"
 msgstr ""
 
-#: lxc/image.go:411
+#: lxc/image.go:420
 msgid "Only https:// is supported for remote image import."
 msgstr ""
 
-#: lxc/help.go:63 lxc/main.go:122
+#: lxc/help.go:63 lxc/main.go:108
 msgid "Options:"
 msgstr ""
 
-#: lxc/image.go:506
+#: lxc/image.go:524
 #, c-format
 msgid "Output is in %s"
 msgstr ""
 <at>  <at>  -1053,23 +1088,23  <at>  <at>  msgstr ""
 msgid "Override the terminal mode (auto, interactive or non-interactive)"
 msgstr ""
 
-#: lxc/list.go:464
+#: lxc/list.go:556
 msgid "PERSISTENT"
 msgstr ""
 
-#: lxc/list.go:381
+#: lxc/list.go:413
 msgid "PID"
 msgstr ""
 
-#: lxc/list.go:382
+#: lxc/list.go:414
 msgid "PROFILES"
 msgstr ""
 
-#: lxc/remote.go:365
+#: lxc/remote.go:378
 msgid "PROTOCOL"
 msgstr ""
 
-#: lxc/image.go:606 lxc/remote.go:366
+#: lxc/image.go:626 lxc/remote.go:379
 msgid "PUBLIC"
 msgstr ""
 
 <at>  <at>  -1091,7 +1126,7  <at>  <at>  msgstr "Alternatives config Verzeichnis."
 msgid "Path to an alternate server directory."
 msgstr "Alternatives config Verzeichnis."
 
-#: lxc/main.go:39
+#: lxc/main.go:31
 msgid "Permisson denied, are you in the lxd group?"
 msgstr ""
 
 <at>  <at>  -1111,11 +1146,11  <at>  <at>  msgstr ""
 "\n"
 "lxd help [—all]\n"
 
-#: lxc/profile.go:216
+#: lxc/profile.go:218
 msgid "Press enter to open the editor again"
 msgstr ""
 
-#: lxc/config.go:501 lxc/config.go:566 lxc/image.go:688
+#: lxc/config.go:532 lxc/config.go:597 lxc/image.go:721
 msgid "Press enter to start the editor again"
 msgstr ""
 
 <at>  <at>  -1147,22 +1182,22  <at>  <at>  msgstr ""
 msgid "Processes: %d"
 msgstr "Profil %s erstellt\n"
 
-#: lxc/profile.go:272
+#: lxc/profile.go:274
 #, fuzzy, c-format
 msgid "Profile %s added to %s"
 msgstr "Profil %s wurde auf %s angewandt\n"
 
-#: lxc/profile.go:167
+#: lxc/profile.go:169
 #, fuzzy, c-format
 msgid "Profile %s created"
 msgstr "Profil %s erstellt\n"
 
-#: lxc/profile.go:237
+#: lxc/profile.go:239
 #, fuzzy, c-format
 msgid "Profile %s deleted"
 msgstr "Profil %s gelöscht\n"
 
-#: lxc/profile.go:303
+#: lxc/profile.go:305
 #, fuzzy, c-format
 msgid "Profile %s removed from %s"
 msgstr "Gerät %s wurde von %s entfernt\n"
 <at>  <at>  -1172,7 +1207,7  <at>  <at>  msgstr "Gerät %s wurde von %s entfernt\n"
 msgid "Profile to apply to the new container"
 msgstr "kann nicht zum selben Container Namen kopieren"
 
-#: lxc/profile.go:253
+#: lxc/profile.go:255
 #, fuzzy, c-format
 msgid "Profiles %s applied to %s"
 msgstr "Profil %s wurde auf %s angewandt\n"
 <at>  <at>  -1182,7 +1217,7  <at>  <at>  msgstr "Profil %s wurde auf %s angewandt\n"
 msgid "Profiles: %s"
 msgstr "Profil %s erstellt\n"
 
-#: lxc/image.go:343
+#: lxc/image.go:352
 #, fuzzy
 msgid "Properties:"
 msgstr "Eigenschaften:\n"
 <at>  <at>  -1191,7 +1226,7  <at>  <at>  msgstr "Eigenschaften:\n"
 msgid "Public image server"
 msgstr ""
 
-#: lxc/image.go:331
+#: lxc/image.go:340
 #, fuzzy, c-format
 msgid "Public: %s"
 msgstr "Öffentlich: %s\n"
 <at>  <at>  -1226,27 +1261,27  <at>  <at>  msgstr ""
 msgid "Retrieving image: %s"
 msgstr ""
 
-#: lxc/image.go:609
+#: lxc/image.go:629
 msgid "SIZE"
 msgstr ""
 
-#: lxc/list.go:383
+#: lxc/list.go:415
 msgid "SNAPSHOTS"
 msgstr ""
 
-#: lxc/list.go:384
+#: lxc/list.go:416
 msgid "STATE"
 msgstr ""
 
-#: lxc/remote.go:367
+#: lxc/remote.go:380
 msgid "STATIC"
 msgstr ""
 
-#: lxc/remote.go:214
+#: lxc/remote.go:227
 msgid "Server certificate NACKed by user"
 msgstr "Server Zertifikat vom Benutzer nicht akzeptiert"
 
-#: lxc/remote.go:276
+#: lxc/remote.go:289
 msgid "Server doesn't trust us after adding our cert"
 msgstr ""
 "Der Server vertraut uns nicht nachdem er unser Zertifikat hinzugefügt hat"
 <at>  <at>  -1289,7 +1324,7  <at>  <at>  msgstr "Zeigt alle Befehle (nicht nur die interessanten)"
 msgid "Show the container's last 100 log lines?"
 msgstr "Zeige die letzten 100 Zeilen Protokoll des Containers?"
 
-#: lxc/image.go:329
+#: lxc/image.go:338
 #, fuzzy, c-format
 msgid "Size: %.2fMB"
 msgstr "Größe: %.2vMB\n"
 <at>  <at>  -1298,7 +1333,7  <at>  <at>  msgstr "Größe: %.2vMB\n"
 msgid "Snapshots:"
 msgstr ""
 
-#: lxc/image.go:353
+#: lxc/image.go:362
 msgid "Source:"
 msgstr ""
 
 <at>  <at>  -1320,7 +1355,7  <at>  <at>  msgstr ""
 msgid "Stopping container failed!"
 msgstr "Anhalten des Containers fehlgeschlagen!"
 
-#: lxc/action.go:39
+#: lxc/action.go:40
 #, fuzzy
 msgid "Store the container state (only for stop)."
 msgstr "Herunterfahren des Containers erzwingen."
 <at>  <at>  -1333,7 +1368,7  <at>  <at>  msgstr ""
 msgid "Swap (peak)"
 msgstr ""
 
-#: lxc/list.go:385
+#: lxc/list.go:417
 msgid "TYPE"
 msgstr ""
 
 <at>  <at>  -1347,8 +1382,8  <at>  <at>  msgid ""
 "restarted."
 msgstr ""
 
-#: lxc/config.go:645 lxc/config.go:657 lxc/config.go:690 lxc/config.go:708
-#: lxc/config.go:746 lxc/config.go:764
+#: lxc/config.go:676 lxc/config.go:688 lxc/config.go:721 lxc/config.go:739
+#: lxc/config.go:777 lxc/config.go:795
 #, fuzzy
 msgid "The device doesn't exist"
 msgstr "entfernte Instanz %s existiert nicht"
 <at>  <at>  -1366,21 +1401,21  <at>  <at>  msgstr ""
 msgid "Time to wait for the container before killing it."
 msgstr "Wartezeit bevor der Container gestoppt wird."
 
-#: lxc/image.go:332
+#: lxc/image.go:341
 #, fuzzy
 msgid "Timestamps:"
 msgstr "Zeitstempel:\n"
 
-#: lxc/main.go:147
+#: lxc/main.go:133
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/image.go:402
+#: lxc/image.go:411
 #, c-format
 msgid "Transferring image: %d%%"
 msgstr ""
 
-#: lxc/action.go:93 lxc/launch.go:132
+#: lxc/action.go:94 lxc/launch.go:132
 #, c-format
 msgid "Try `lxc info --show-log %s` for more info"
 msgstr ""
 <at>  <at>  -1393,24 +1428,24  <at>  <at>  msgstr ""
 msgid "Type: persistent"
 msgstr ""
 
-#: lxc/image.go:610
+#: lxc/image.go:630
 msgid "UPLOAD DATE"
 msgstr ""
 
-#: lxc/remote.go:364
+#: lxc/remote.go:377
 msgid "URL"
 msgstr ""
 
-#: lxc/remote.go:82
+#: lxc/remote.go:97
 msgid "Unable to read remote TLS certificate"
 msgstr ""
 
-#: lxc/image.go:337
+#: lxc/image.go:346
 #, c-format
 msgid "Uploaded: %s"
 msgstr ""
 
-#: lxc/main.go:122
+#: lxc/main.go:108
 #, fuzzy, c-format
 msgid "Usage: %s"
 msgstr ""
 <at>  <at>  -1446,11 +1481,11  <at>  <at>  msgstr "Zustand des laufenden Containers sichern oder nicht"
 msgid "Whether to show the expanded configuration"
 msgstr ""
 
-#: lxc/remote.go:339 lxc/remote.go:344
+#: lxc/remote.go:352 lxc/remote.go:357
 msgid "YES"
 msgstr ""
 
-#: lxc/main.go:66
+#: lxc/main.go:52
 msgid "`lxc config profile` is deprecated, please use `lxc profile`"
 msgstr ""
 
 <at>  <at>  -1459,19 +1494,19  <at>  <at>  msgid "bad number of things scanned from image, container or snapshot"
 msgstr ""
 "Falsche Anzahl an Objekten im Abbild, Container oder Sicherungspunkt gelesen."
 
-#: lxc/action.go:89
+#: lxc/action.go:90
 msgid "bad result type from action"
 msgstr ""
 
-#: lxc/copy.go:78
+#: lxc/copy.go:99
 msgid "can't copy to the same container name"
 msgstr "kann nicht zum selben Container Namen kopieren"
 
-#: lxc/remote.go:327
+#: lxc/remote.go:340
 msgid "can't remove the default remote"
 msgstr ""
 
-#: lxc/remote.go:353
+#: lxc/remote.go:366
 msgid "default"
 msgstr ""
 
 <at>  <at>  -1479,20 +1514,20  <at>  <at>  msgstr ""
 msgid "didn't get any affected image, container or snapshot from server"
 msgstr ""
 
-#: lxc/image.go:323
+#: lxc/image.go:332
 msgid "disabled"
 msgstr ""
 
-#: lxc/image.go:325
+#: lxc/image.go:334
 msgid "enabled"
 msgstr ""
 
-#: lxc/main.go:25 lxc/main.go:159
+#: lxc/main.go:22 lxc/main.go:144
 #, fuzzy, c-format
 msgid "error: %v"
 msgstr "Fehler: %v\n"
 
-#: lxc/help.go:40 lxc/main.go:117
+#: lxc/help.go:40 lxc/main.go:103
 #, fuzzy, c-format
 msgid "error: unknown command: %s"
 msgstr "Fehler: unbekannter Befehl: %s\n"
 <at>  <at>  -1501,40 +1536,40  <at>  <at>  msgstr "Fehler: unbekannter Befehl: %s\n"
 msgid "got bad version"
 msgstr "Versionskonflikt"
 
-#: lxc/image.go:318 lxc/image.go:586
+#: lxc/image.go:327 lxc/image.go:606
 msgid "no"
 msgstr ""
 
-#: lxc/copy.go:101
+#: lxc/copy.go:122
 msgid "not all the profiles from the source exist on the target"
 msgstr "nicht alle Profile der Quelle sind am Ziel vorhanden."
 
-#: lxc/remote.go:207
+#: lxc/remote.go:220
 #, fuzzy
 msgid "ok (y/n)?"
 msgstr "OK (y/n)? "
 
-#: lxc/main.go:266 lxc/main.go:270
+#: lxc/main.go:277 lxc/main.go:281
 #, c-format
 msgid "processing aliases failed %s\n"
 msgstr ""
 
-#: lxc/remote.go:389
+#: lxc/remote.go:402
 #, c-format
 msgid "remote %s already exists"
 msgstr "entfernte Instanz %s existiert bereits"
 
-#: lxc/remote.go:319 lxc/remote.go:381 lxc/remote.go:416 lxc/remote.go:432
+#: lxc/remote.go:332 lxc/remote.go:394 lxc/remote.go:429 lxc/remote.go:445
 #, c-format
 msgid "remote %s doesn't exist"
 msgstr "entfernte Instanz %s existiert nicht"
 
-#: lxc/remote.go:302
+#: lxc/remote.go:315
 #, c-format
 msgid "remote %s exists as <%s>"
 msgstr "entfernte Instanz %s existiert als <%s>"
 
-#: lxc/remote.go:323 lxc/remote.go:385 lxc/remote.go:420
+#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433
 #, c-format
 msgid "remote %s is static and cannot be modified"
 msgstr ""
 <at>  <at>  -1556,11 +1591,11  <at>  <at>  msgstr ""
 msgid "unreachable return reached"
 msgstr ""
 
-#: lxc/main.go:199
+#: lxc/main.go:209
 msgid "wrong number of subcommand arguments"
 msgstr "falsche Anzahl an Parametern für Unterbefehl"
 
-#: lxc/delete.go:45 lxc/image.go:320 lxc/image.go:590
+#: lxc/delete.go:45 lxc/image.go:329 lxc/image.go:610
 msgid "yes"
 msgstr ""
 
diff --git a/po/fr.po b/po/fr.po
index 90f536b..6d5e56e 100644
--- a/po/fr.po
+++ b/po/fr.po
 <at>  <at>  -7,7 +7,7  <at>  <at>  msgid ""
 msgstr ""
 "Project-Id-Version: LXD\n"
 "Report-Msgid-Bugs-To: lxc-devel <at> lists.linuxcontainers.org\n"
-"POT-Creation-Date: 2016-04-25 14:47-0500\n"
+"POT-Creation-Date: 2016-06-24 08:22-0700\n"
 "PO-Revision-Date: 2015-02-26 02:05-0600\n"
 "Last-Translator: Stéphane Graber <stgraber <at> ubuntu.com\n"
 "Language-Team: French <fr <at> li.org>\n"
 <at>  <at>  -49,7 +49,7  <at>  <at>  msgid ""
 "### Note that the name is shown but cannot be changed"
 msgstr ""
 
-#: lxc/image.go:83
+#: lxc/image.go:85
 msgid ""
 "### This is a yaml representation of the image properties.\n"
 "### Any line starting with a '# will be ignored.\n"
 <at>  <at>  -80,7 +80,7  <at>  <at>  msgid ""
 "### Note that the name is shown but cannot be changed"
 msgstr ""
 
-#: lxc/image.go:583
+#: lxc/image.go:603
 #, c-format
 msgid "%s (%d more)"
 msgstr ""
 <at>  <at>  -90,19 +90,19  <at>  <at>  msgstr ""
 msgid "'/' not allowed in snapshot name"
 msgstr "'/' n'est pas autorisé dans le nom d'un instantané (snapshot)\n"
 
-#: lxc/profile.go:251
+#: lxc/profile.go:253
 msgid "(none)"
 msgstr ""
 
-#: lxc/image.go:604 lxc/image.go:633
+#: lxc/image.go:624 lxc/image.go:666
 msgid "ALIAS"
 msgstr ""
 
-#: lxc/image.go:608
+#: lxc/image.go:628
 msgid "ARCH"
 msgstr ""
 
-#: lxc/list.go:378
+#: lxc/list.go:409
 msgid "ARCHITECTURE"
 msgstr ""
 
 <at>  <at>  -110,12 +110,12  <at>  <at>  msgstr ""
 msgid "Accept certificate"
 msgstr ""
 
-#: lxc/remote.go:256
+#: lxc/remote.go:269
 #, c-format
 msgid "Admin password for %s: "
 msgstr "Mot de passe administrateur pour %s: "
 
-#: lxc/image.go:347
+#: lxc/image.go:356
 msgid "Aliases:"
 msgstr ""
 
 <at>  <at>  -123,12 +123,12  <at>  <at>  msgstr ""
 msgid "An environment variable of the form HOME=/home/foo"
 msgstr ""
 
-#: lxc/image.go:330 lxc/info.go:90
+#: lxc/image.go:339 lxc/info.go:90
 #, c-format
 msgid "Architecture: %s"
 msgstr ""
 
-#: lxc/image.go:351
+#: lxc/image.go:360
 #, c-format
 msgid "Auto update: %s"
 msgstr ""
 <at>  <at>  -145,11 +145,11  <at>  <at>  msgstr ""
 msgid "Bytes sent"
 msgstr ""
 
-#: lxc/config.go:270
+#: lxc/config.go:274
 msgid "COMMON NAME"
 msgstr ""
 
-#: lxc/list.go:379
+#: lxc/list.go:410
 msgid "CREATED AT"
 msgstr ""
 
 <at>  <at>  -163,11 +163,11  <at>  <at>  msgstr ""
 msgid "Can't unset key '%s', it's not currently set."
 msgstr ""
 
-#: lxc/profile.go:417
+#: lxc/profile.go:419
 msgid "Cannot provide container name to list"
 msgstr ""
 
-#: lxc/remote.go:206
+#: lxc/remote.go:219
 #, fuzzy, c-format
 msgid "Certificate fingerprint: %x"
 msgstr "Empreinte du certificat: % x\n"
 <at>  <at>  -180,11 +180,11  <at>  <at>  msgid ""
 "lxc %s <name> [<name>...]"
 msgstr "Change l'état du conteneur à %s.\n"
 
-#: lxc/remote.go:279
+#: lxc/remote.go:292
 msgid "Client certificate stored at server: "
 msgstr "Certificat client enregistré avec le serveur: "
 
-#: lxc/list.go:99 lxc/list.go:100
+#: lxc/list.go:121 lxc/list.go:122
 msgid "Columns"
 msgstr ""
 
 <at>  <at>  -192,12 +192,12  <at>  <at>  msgstr ""
 msgid "Config key/value to apply to the new container"
 msgstr ""
 
-#: lxc/config.go:500 lxc/config.go:565 lxc/image.go:687 lxc/profile.go:215
+#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:720 lxc/profile.go:217
 #, fuzzy, c-format
 msgid "Config parsing error: %s"
 msgstr "erreur: %v\n"
 
-#: lxc/main.go:37
+#: lxc/main.go:29
 msgid "Connection refused; is LXD running?"
 msgstr ""
 
 <at>  <at>  -215,7 +215,7  <at>  <at>  msgstr ""
 msgid "Container published with fingerprint: %s"
 msgstr "Empreinte du certificat: % x\n"
 
-#: lxc/image.go:155
+#: lxc/image.go:157
 msgid "Copy aliases from source"
 msgstr ""
 
 <at>  <at>  -227,12 +227,12  <at>  <at>  msgid ""
 "ephemeral|e]"
 msgstr ""
 
-#: lxc/image.go:268
+#: lxc/image.go:271
 #, c-format
 msgid "Copying the image: %s"
 msgstr ""
 
-#: lxc/remote.go:221
+#: lxc/remote.go:234
 msgid "Could not create server cert dir"
 msgstr "Le dossier de stockage des certificats serveurs n'a pas pû être créé"
 
 <at>  <at>  -256,7 +256,7  <at>  <at>  msgid ""
 "lxc snapshot u1 snap0"
 msgstr ""
 
-#: lxc/image.go:335 lxc/info.go:92
+#: lxc/image.go:344 lxc/info.go:92
 #, c-format
 msgid "Created: %s"
 msgstr ""
 <at>  <at>  -270,7 +270,7  <at>  <at>  msgstr ""
 msgid "Creating the container"
 msgstr ""
 
-#: lxc/image.go:607 lxc/image.go:635
+#: lxc/image.go:627 lxc/image.go:668
 msgid "DESCRIPTION"
 msgstr ""
 
 <at>  <at>  -285,29 +285,29  <at>  <at>  msgid ""
 "snapshots, ...)."
 msgstr ""
 
-#: lxc/config.go:617
+#: lxc/config.go:648
 #, c-format
 msgid "Device %s added to %s"
 msgstr ""
 
-#: lxc/config.go:804
+#: lxc/config.go:835
 #, c-format
 msgid "Device %s removed from %s"
 msgstr ""
 
-#: lxc/list.go:462
+#: lxc/list.go:554
 msgid "EPHEMERAL"
 msgstr ""
 
-#: lxc/config.go:272
+#: lxc/config.go:276
 msgid "EXPIRY DATE"
 msgstr ""
 
-#: lxc/main.go:55
+#: lxc/main.go:41
 msgid "Enables debug mode."
 msgstr "Active le mode de déboguage."
 
-#: lxc/main.go:54
+#: lxc/main.go:40
 msgid "Enables verbose mode."
 msgstr "Active le mode verbeux."
 
 <at>  <at>  -336,24 +336,24  <at>  <at>  msgid ""
 "AND stdout are terminals (stderr is ignored)."
 msgstr "Exécute la commande spécifiée dans un conteneur.\n"
 
-#: lxc/image.go:339
+#: lxc/image.go:348
 #, c-format
 msgid "Expires: %s"
 msgstr ""
 
-#: lxc/image.go:341
+#: lxc/image.go:350
 msgid "Expires: never"
 msgstr ""
 
-#: lxc/config.go:269 lxc/image.go:605 lxc/image.go:634
+#: lxc/config.go:273 lxc/image.go:625 lxc/image.go:667
 msgid "FINGERPRINT"
 msgstr ""
 
-#: lxc/list.go:102
+#: lxc/list.go:124
 msgid "Fast mode (same as --columns=nsacPt"
 msgstr ""
 
-#: lxc/image.go:328
+#: lxc/image.go:337
 #, fuzzy, c-format
 msgid "Fingerprint: %s"
 msgstr "Empreinte du certificat: % x\n"
 <at>  <at>  -366,7 +366,7  <at>  <at>  msgid ""
 "lxc finger <remote>"
 msgstr "Contacte LXD pour voir s'il est fonctionel.\n"
 
-#: lxc/action.go:37
+#: lxc/action.go:37 lxc/action.go:38
 msgid "Force the container to shutdown."
 msgstr "Force l'arrêt du conteneur."
 
 <at>  <at>  -374,50 +374,50  <at>  <at>  msgstr "Force l'arrêt du conteneur."
 msgid "Force the removal of stopped containers."
 msgstr ""
 
-#: lxc/main.go:56
+#: lxc/main.go:42
 msgid "Force using the local unix socket."
 msgstr ""
 
-#: lxc/list.go:101
+#: lxc/image.go:160 lxc/list.go:123
 msgid "Format"
 msgstr ""
 
-#: lxc/main.go:138
+#: lxc/remote.go:67
 #, fuzzy
 msgid "Generating a client certificate. This may take a minute..."
 msgstr "Géneration d'un certificat client. Ceci peut prendre une minute...\n"
 
-#: lxc/list.go:376
+#: lxc/list.go:407
 msgid "IPV4"
 msgstr ""
 
-#: lxc/list.go:377
+#: lxc/list.go:408
 msgid "IPV6"
 msgstr ""
 
-#: lxc/config.go:271
+#: lxc/config.go:275
 msgid "ISSUE DATE"
 msgstr ""
 
-#: lxc/main.go:146
+#: lxc/main.go:132
 msgid ""
 "If this is your first time using LXD, you should also run: sudo lxd init"
 msgstr ""
 
-#: lxc/main.go:57
+#: lxc/main.go:43
 msgid "Ignore aliases when determining what command to run."
 msgstr ""
 
-#: lxc/action.go:40
+#: lxc/action.go:41
 #, fuzzy
 msgid "Ignore the container state (only for start)."
 msgstr "Force l'arrêt du conteneur."
 
-#: lxc/image.go:273
+#: lxc/image.go:276
 msgid "Image copied successfully!"
 msgstr ""
 
-#: lxc/image.go:419
+#: lxc/image.go:428
 #, fuzzy, c-format
 msgid "Image imported with fingerprint: %s"
 msgstr "Empreinte du certificat: % x\n"
 <at>  <at>  -438,17 +438,22  <at>  <at>  msgid ""
 "lxc init ubuntu u1"
 msgstr ""
 
-#: lxc/remote.go:122
+#: lxc/remote.go:137
 #, c-format
 msgid "Invalid URL scheme \"%s\" in \"%s\""
 msgstr ""
 
+#: lxc/config.go:254
+#, fuzzy
+msgid "Invalid certificate"
+msgstr "Gérer la configuration.\n"
+
 #: lxc/init.go:30 lxc/init.go:35
 #, fuzzy
 msgid "Invalid configuration key"
 msgstr "Gérer la configuration.\n"
 
-#: lxc/file.go:190
+#: lxc/file.go:195
 #, c-format
 msgid "Invalid source %s"
 msgstr "Source invalide %s"
 <at>  <at>  -462,11 +467,15  <at>  <at>  msgstr "Destination invalide %s"
 msgid "Ips:"
 msgstr ""
 
-#: lxc/image.go:156
+#: lxc/image.go:158
 msgid "Keep the image up to date after initial copy"
 msgstr ""
 
-#: lxc/main.go:35
+#: lxc/list.go:411
+msgid "LAST USED AT"
+msgstr ""
+
+#: lxc/main.go:27
 msgid "LXD socket not found; is LXD installed and running?"
 msgstr ""
 
 <at>  <at>  -497,7 +506,7  <at>  <at>  msgid ""
 " lxc info [<remote>:]"
 msgstr ""
 
-#: lxc/list.go:67
+#: lxc/list.go:68
 msgid ""
 "Lists the available resources.\n"
 "\n"
 <at>  <at>  -517,11 +526,18  <at>  <at>  msgid ""
 "* A regular expression matching a configuration item or its value. (e.g. "
 "volatile.eth0.hwaddr=00:16:3e:.*)\n"
 "\n"
-"Columns for table format are:\n"
+"The -c option takes a comma separated list of arguments that control\n"
+"which container attributes to output when displaying in table format.\n"
+"Column arguments are either pre-defined shorthand chars (see below),\n"
+"or (extended) config keys.  Commas between consecutive shorthand chars\n"
+"are optional.\n"
+"\n"
+"Pre-defined shorthand chars:\n"
 "* 4 - IPv4 address\n"
 "* 6 - IPv6 address\n"
 "* a - architecture\n"
 "* c - creation date\n"
+"* l - last used date\n"
 "* n - name\n"
 "* p - pid of container init process\n"
 "* P - profiles\n"
 <at>  <at>  -529,15 +545,29  <at>  <at>  msgid ""
 "* S - number of snapshots\n"
 "* t - type (persistent or ephemeral)\n"
 "\n"
+"Config key syntax: key[:name][:maxWidth]\n"
+"* key      - The (extended) config key to display\n"
+"* name     - Name to display in the column header, defaults to the key\n"
+"             if not specified or if empty (to allow defining maxWidth\n"
+"             without a custom name, e.g. user.key::0)\n"
+"* maxWidth - Max width of the column (longer results are truncated).\n"
+"             -1 == unlimited\n"
+"              0 == width of column header\n"
+"             >0 == max width in chars\n"
+"             Default is -1 (unlimited)\n"
+"\n"
 "Default column layout: ns46tS\n"
-"Fast column layout: nsacPt"
+"Fast column layout: nsacPt\n"
+"\n"
+"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile."
+"eth0.hwaddr:MAC\n"
 msgstr ""
 
 #: lxc/info.go:225
 msgid "Log:"
 msgstr ""
 
-#: lxc/image.go:154
+#: lxc/image.go:156
 msgid "Make image public"
 msgstr ""
 
 <at>  <at>  -563,16 +593,21  <at>  <at>  msgid ""
 "             cat profile.yml | lxc profile edit <profile> # read from "
 "profile.yml\n"
 "\n"
-"lxc profile apply <container> <profiles>\n"
-"    Apply a comma-separated list of profiles to a container, in order.\n"
+"lxc profile assign <container> <profiles>\n"
+"    Assign a comma-separated list of profiles to a container, in order.\n"
 "    All profiles passed in this call (and only those) will be applied\n"
-"    to the specified container.\n"
-"    Example: lxc profile apply foo default,bar # Apply default and bar\n"
-"             lxc profile apply foo default # Only default is active\n"
-"             lxc profile apply '' # no profiles are applied anymore\n"
-"             lxc profile apply bar,default # Apply default second now\n"
-"lxc profile apply-add <container> <profile>\n"
-"lxc profile apply-remove <container> <profile>\n"
+"    to the specified container, i.e. it sets the list of profiles exactly "
+"to\n"
+"    those specified in this command. To add/remove a particular profile from "
+"a\n"
+"    container, use {add|remove} below.\n"
+"    Example: lxc profile assign foo default,bar # Apply default and bar\n"
+"             lxc profile assign foo default # Only default is active\n"
+"             lxc profile assign '' # no profiles are applied anymore\n"
+"             lxc profile assign bar,default # Apply default second now\n"
+"lxc profile add <container> <profile> # add a profile to a container\n"
+"lxc profile remove <container> <profile> # remove the profile from a "
+"container\n"
 "\n"
 "Devices:\n"
 "lxc profile device list <profile>                                   List "
 <at>  <at>  -686,7 +721,7  <at>  <at>  msgid ""
 "Print the default remote."
 msgstr ""
 
-#: lxc/image.go:93
+#: lxc/image.go:95
 msgid ""
 "Manipulate container images.\n"
 "\n"
 <at>  <at>  -717,8 +752,8  <at>  <at>  msgid ""
 "    The auto-update flag instructs the server to keep this image up to\n"
 "    date. It requires the source to be an alias and for it to be public.\n"
 "\n"
-"lxc image delete [remote:]<image>\n"
-"    Delete an image from the LXD image store.\n"
+"lxc image delete [remote:]<image> [remote:][<image>...]\n"
+"    Delete one or more images from the LXD image store.\n"
 "\n"
 "lxc image export [remote:]<image>\n"
 "    Export an image from the LXD image store into a distributable tarball.\n"
 <at>  <at>  -726,7 +761,7  <at>  <at>  msgid ""
 "lxc image info [remote:]<image>\n"
 "    Print everything LXD knows about a given image.\n"
 "\n"
-"lxc image list [remote:] [filter]\n"
+"lxc image list [remote:] [filter] [--format table|json]\n"
 "    List images in the LXD image store. Filters may be of the\n"
 "    <key>=<value> form for property based filtering, or part of the image\n"
 "    hash or part of the image alias name.\n"
 <at>  <at>  -777,7 +812,7  <at>  <at>  msgid ""
 "lxc monitor --type=logging"
 msgstr ""
 
-#: lxc/file.go:178
+#: lxc/file.go:183
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 "Plusieurs fichiers à télécharger mais la destination n'est pas un dossier"
 <at>  <at>  -794,15 +829,15  <at>  <at>  msgid ""
 "    Rename a local container.\n"
 msgstr ""
 
-#: lxc/action.go:63
+#: lxc/action.go:64
 msgid "Must supply container name for: "
 msgstr ""
 
-#: lxc/list.go:380 lxc/remote.go:363
+#: lxc/list.go:412 lxc/remote.go:376
 msgid "NAME"
 msgstr ""
 
-#: lxc/remote.go:337 lxc/remote.go:342
+#: lxc/remote.go:350 lxc/remote.go:355
 msgid "NO"
 msgstr ""
 
 <at>  <at>  -811,33 +846,33  <at>  <at>  msgstr ""
 msgid "Name: %s"
 msgstr ""
 
-#: lxc/image.go:157 lxc/publish.go:33
+#: lxc/image.go:159 lxc/publish.go:33
 msgid "New alias to define at target"
 msgstr ""
 
-#: lxc/config.go:281
+#: lxc/config.go:285
 #, fuzzy
 msgid "No certificate provided to add"
 msgstr "Un certificat n'a pas été fournis"
 
-#: lxc/config.go:304
+#: lxc/config.go:308
 msgid "No fingerprint specified."
 msgstr "Aucune empreinte n'a été spécifié."
 
-#: lxc/remote.go:107
+#: lxc/remote.go:122
 msgid "Only https URLs are supported for simplestreams"
 msgstr ""
 
-#: lxc/image.go:411
+#: lxc/image.go:420
 msgid "Only https:// is supported for remote image import."
 msgstr ""
 
-#: lxc/help.go:63 lxc/main.go:122
+#: lxc/help.go:63 lxc/main.go:108
 #, fuzzy
 msgid "Options:"
 msgstr "Opération %s"
 
-#: lxc/image.go:506
+#: lxc/image.go:524
 #, c-format
 msgid "Output is in %s"
 msgstr ""
 <at>  <at>  -846,23 +881,23  <at>  <at>  msgstr ""
 msgid "Override the terminal mode (auto, interactive or non-interactive)"
 msgstr ""
 
-#: lxc/list.go:464
+#: lxc/list.go:556
 msgid "PERSISTENT"
 msgstr ""
 
-#: lxc/list.go:381
+#: lxc/list.go:413
 msgid "PID"
 msgstr ""
 
-#: lxc/list.go:382
+#: lxc/list.go:414
 msgid "PROFILES"
 msgstr ""
 
-#: lxc/remote.go:365
+#: lxc/remote.go:378
 msgid "PROTOCOL"
 msgstr ""
 
-#: lxc/image.go:606 lxc/remote.go:366
+#: lxc/image.go:626 lxc/remote.go:379
 msgid "PUBLIC"
 msgstr ""
 
 <at>  <at>  -884,7 +919,7  <at>  <at>  msgstr "Dossier de configuration alternatif."
 msgid "Path to an alternate server directory."
 msgstr "Dossier de configuration alternatif."
 
-#: lxc/main.go:39
+#: lxc/main.go:31
 msgid "Permisson denied, are you in the lxd group?"
 msgstr ""
 
 <at>  <at>  -901,11 +936,11  <at>  <at>  msgid ""
 "lxd help [--all]"
 msgstr "Explique comment utiliser LXD.\n"
 
-#: lxc/profile.go:216
+#: lxc/profile.go:218
 msgid "Press enter to open the editor again"
 msgstr ""
 
-#: lxc/config.go:501 lxc/config.go:566 lxc/image.go:688
+#: lxc/config.go:532 lxc/config.go:597 lxc/image.go:721
 msgid "Press enter to start the editor again"
 msgstr ""
 
 <at>  <at>  -934,22 +969,22  <at>  <at>  msgstr "Montre le numéro de version de LXD.\n"
 msgid "Processes: %d"
 msgstr "Mauvaise URL pour le conteneur %s"
 
-#: lxc/profile.go:272
+#: lxc/profile.go:274
 #, fuzzy, c-format
 msgid "Profile %s added to %s"
 msgstr "Mauvaise URL pour le conteneur %s"
 
-#: lxc/profile.go:167
+#: lxc/profile.go:169
 #, c-format
 msgid "Profile %s created"
 msgstr ""
 
-#: lxc/profile.go:237
+#: lxc/profile.go:239
 #, c-format
 msgid "Profile %s deleted"
 msgstr ""
 
-#: lxc/profile.go:303
+#: lxc/profile.go:305
 #, c-format
 msgid "Profile %s removed from %s"
 msgstr ""
 <at>  <at>  -958,7 +993,7  <at>  <at>  msgstr ""
 msgid "Profile to apply to the new container"
 msgstr ""
 
-#: lxc/profile.go:253
+#: lxc/profile.go:255
 #, c-format
 msgid "Profiles %s applied to %s"
 msgstr ""
 <at>  <at>  -968,7 +1003,7  <at>  <at>  msgstr ""
 msgid "Profiles: %s"
 msgstr "Mauvaise URL pour le conteneur %s"
 
-#: lxc/image.go:343
+#: lxc/image.go:352
 msgid "Properties:"
 msgstr ""
 
 <at>  <at>  -976,7 +1011,7  <at>  <at>  msgstr ""
 msgid "Public image server"
 msgstr ""
 
-#: lxc/image.go:331
+#: lxc/image.go:340
 #, c-format
 msgid "Public: %s"
 msgstr ""
 <at>  <at>  -1011,27 +1046,27  <at>  <at>  msgstr ""
 msgid "Retrieving image: %s"
 msgstr ""
 
-#: lxc/image.go:609
+#: lxc/image.go:629
 msgid "SIZE"
 msgstr ""
 
-#: lxc/list.go:383
+#: lxc/list.go:415
 msgid "SNAPSHOTS"
 msgstr ""
 
-#: lxc/list.go:384
+#: lxc/list.go:416
 msgid "STATE"
 msgstr ""
 
-#: lxc/remote.go:367
+#: lxc/remote.go:380
 msgid "STATIC"
 msgstr ""
 
-#: lxc/remote.go:214
+#: lxc/remote.go:227
 msgid "Server certificate NACKed by user"
 msgstr "Le certificat serveur a été rejeté par l'utilisateur"
 
-#: lxc/remote.go:276
+#: lxc/remote.go:289
 msgid "Server doesn't trust us after adding our cert"
 msgstr "Identification refuse après l'ajout du certificat client"
 
 <at>  <at>  -1073,7 +1108,7  <at>  <at>  msgstr "Affiche toutes les comandes (pas seulement les intéresantes)"
 msgid "Show the container's last 100 log lines?"
 msgstr ""
 
-#: lxc/image.go:329
+#: lxc/image.go:338
 #, c-format
 msgid "Size: %.2fMB"
 msgstr ""
 <at>  <at>  -1082,7 +1117,7  <at>  <at>  msgstr ""
 msgid "Snapshots:"
 msgstr ""
 
-#: lxc/image.go:353
+#: lxc/image.go:362
 msgid "Source:"
 msgstr ""
 
 <at>  <at>  -1104,7 +1139,7  <at>  <at>  msgstr ""
 msgid "Stopping container failed!"
 msgstr "L'arrêt du conteneur a échoué!"
 
-#: lxc/action.go:39
+#: lxc/action.go:40
 #, fuzzy
 msgid "Store the container state (only for stop)."
 msgstr "Force l'arrêt du conteneur."
 <at>  <at>  -1117,7 +1152,7  <at>  <at>  msgstr ""
 msgid "Swap (peak)"
 msgstr ""
 
-#: lxc/list.go:385
+#: lxc/list.go:417
 msgid "TYPE"
 msgstr ""
 
 <at>  <at>  -1131,8 +1166,8  <at>  <at>  msgid ""
 "restarted."
 msgstr ""
 
-#: lxc/config.go:645 lxc/config.go:657 lxc/config.go:690 lxc/config.go:708
-#: lxc/config.go:746 lxc/config.go:764
+#: lxc/config.go:676 lxc/config.go:688 lxc/config.go:721 lxc/config.go:739
+#: lxc/config.go:777 lxc/config.go:795
 #, fuzzy
 msgid "The device doesn't exist"
 msgstr "le serveur distant %s n'existe pas"
 <at>  <at>  -1150,20 +1185,20  <at>  <at>  msgstr ""
 msgid "Time to wait for the container before killing it."
 msgstr "Temps d'attente avant de tuer le conteneur."
 
-#: lxc/image.go:332
+#: lxc/image.go:341
 msgid "Timestamps:"
 msgstr ""
 
-#: lxc/main.go:147
+#: lxc/main.go:133
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
 
-#: lxc/image.go:402
+#: lxc/image.go:411
 #, c-format
 msgid "Transferring image: %d%%"
 msgstr ""
 
-#: lxc/action.go:93 lxc/launch.go:132
+#: lxc/action.go:94 lxc/launch.go:132
 #, c-format
 msgid "Try `lxc info --show-log %s` for more info"
 msgstr ""
 <at>  <at>  -1176,24 +1211,24  <at>  <at>  msgstr ""
 msgid "Type: persistent"
 msgstr ""
 
-#: lxc/image.go:610
+#: lxc/image.go:630
 msgid "UPLOAD DATE"
 msgstr ""
 
-#: lxc/remote.go:364
+#: lxc/remote.go:377
 msgid "URL"
 msgstr ""
 
-#: lxc/remote.go:82
+#: lxc/remote.go:97
 msgid "Unable to read remote TLS certificate"
 msgstr ""
 
-#: lxc/image.go:337
+#: lxc/image.go:346
 #, c-format
 msgid "Uploaded: %s"
 msgstr ""
 
-#: lxc/main.go:122
+#: lxc/main.go:108
 #, fuzzy, c-format
 msgid "Usage: %s"
 msgstr ""
 <at>  <at>  -1232,11 +1267,11  <at>  <at>  msgstr ""
 msgid "Whether to show the expanded configuration"
 msgstr ""
 
-#: lxc/remote.go:339 lxc/remote.go:344
+#: lxc/remote.go:352 lxc/remote.go:357
 msgid "YES"
 msgstr ""
 
-#: lxc/main.go:66
+#: lxc/main.go:52
 msgid "`lxc config profile` is deprecated, please use `lxc profile`"
 msgstr ""
 
 <at>  <at>  -1245,19 +1280,19  <at>  <at>  msgstr ""
 msgid "bad number of things scanned from image, container or snapshot"
 msgstr "nombre de propriété invalide pour la ressource"
 
-#: lxc/action.go:89
+#: lxc/action.go:90
 msgid "bad result type from action"
 msgstr "mauvais type de réponse pour l'action!"
 
-#: lxc/copy.go:78
+#: lxc/copy.go:99
 msgid "can't copy to the same container name"
 msgstr ""
 
-#: lxc/remote.go:327
+#: lxc/remote.go:340
 msgid "can't remove the default remote"
 msgstr ""
 
-#: lxc/remote.go:353
+#: lxc/remote.go:366
 msgid "default"
 msgstr ""
 
 <at>  <at>  -1266,20 +1301,20  <at>  <at>  msgstr ""
 msgid "didn't get any affected image, container or snapshot from server"
 msgstr "N'a pas pû obtenir de resource du serveur"
 
-#: lxc/image.go:323
+#: lxc/image.go:332
 msgid "disabled"
 msgstr ""
 
-#: lxc/image.go:325
+#: lxc/image.go:334
 msgid "enabled"
 msgstr ""
 
-#: lxc/main.go:25 lxc/main.go:159
+#: lxc/main.go:22 lxc/main.go:144
 #, fuzzy, c-format
 msgid "error: %v"
 msgstr "erreur: %v\n"
 
-#: lxc/help.go:40 lxc/main.go:117
+#: lxc/help.go:40 lxc/main.go:103
 #, fuzzy, c-format
 msgid "error: unknown command: %s"
 msgstr "erreur: comande inconnue: %s\n"
 <at>  <at>  -1288,40 +1323,40  <at>  <at>  msgstr "erreur: comande inconnue: %s\n"
 msgid "got bad version"
 msgstr "reçu une version invalide"
 
-#: lxc/image.go:318 lxc/image.go:586
+#: lxc/image.go:327 lxc/image.go:606
 msgid "no"
 msgstr ""
 
-#: lxc/copy.go:101
+#: lxc/copy.go:122
 msgid "not all the profiles from the source exist on the target"
 msgstr ""
 
-#: lxc/remote.go:207
+#: lxc/remote.go:220
 #, fuzzy
 msgid "ok (y/n)?"
 msgstr "ok (y/n)?"
 
-#: lxc/main.go:266 lxc/main.go:270
+#: lxc/main.go:277 lxc/main.go:281
 #, c-format
 msgid "processing aliases failed %s\n"
 msgstr ""
 
-#: lxc/remote.go:389
+#: lxc/remote.go:402
 #, c-format
 msgid "remote %s already exists"
 msgstr "le serveur distant %s existe déjà"
 
-#: lxc/remote.go:319 lxc/remote.go:381 lxc/remote.go:416 lxc/remote.go:432
+#: lxc/remote.go:332 lxc/remote.go:394 lxc/remote.go:429 lxc/remote.go:445
 #, c-format
 msgid "remote %s doesn't exist"
 msgstr "le serveur distant %s n'existe pas"
 
-#: lxc/remote.go:302
+#: lxc/remote.go:315
 #, c-format
 msgid "remote %s exists as <%s>"
 msgstr "le serveur distant %s existe en tant que <%s>"
 
-#: lxc/remote.go:323 lxc/remote.go:385 lxc/remote.go:420
+#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433
 #, c-format
 msgid "remote %s is static and cannot be modified"
 msgstr ""
 <at>  <at>  -1343,11 +1378,11  <at>  <at>  msgstr ""
 msgid "unreachable return reached"
 msgstr "Un retour inacessible à été atteint"
 
-#: lxc/main.go:199
+#: lxc/main.go:209
 msgid "wrong number of subcommand arguments"
 msgstr "nombre d'argument incorrect pour la sous-comande"
 
-#: lxc/delete.go:45 lxc/image.go:320 lxc/image.go:590
+#: lxc/delete.go:45 lxc/image.go:329 lxc/image.go:610
 msgid "yes"
 msgstr ""
 
diff --git a/po/ja.po b/po/ja.po
index 4a65d63..bbd485e 100644
--- a/po/ja.po
+++ b/po/ja.po
 <at>  <at>  -7,7 +7,7  <at>  <at>  msgid ""
 msgstr ""
 "Project-Id-Version: LXD\n"
 "Report-Msgid-Bugs-To: lxc-devel <at> lists.linuxcontainers.org\n"
-"POT-Creation-Date: 2016-04-25 14:47-0500\n"
+"POT-Creation-Date: 2016-06-24 08:22-0700\n"
 "PO-Revision-Date: 2016-04-26 14:31+0900\n"
 "Last-Translator: KATOH Yasufumi <karma <at> jazz.email.ne.jp>\n"
 "Language-Team: Japanese <lxd-ja-language-team <at> googlegroups.com>\n"
 <at>  <at>  -49,7 +49,7  <at>  <at>  msgid ""
 "### Note that the name is shown but cannot be changed"
 msgstr ""
 
-#: lxc/image.go:83
+#: lxc/image.go:85
 msgid ""
 "### This is a yaml representation of the image properties.\n"
 "### Any line starting with a '# will be ignored.\n"
 <at>  <at>  -80,7 +80,7  <at>  <at>  msgid ""
 "### Note that the name is shown but cannot be changed"
 msgstr ""
 
-#: lxc/image.go:583
+#: lxc/image.go:603
 #, c-format
 msgid "%s (%d more)"
 msgstr ""
 <at>  <at>  -89,19 +89,19  <at>  <at>  msgstr ""
 msgid "'/' not allowed in snapshot name"
 msgstr "'/' はスナップショットの名前には使用できません。"
 
-#: lxc/profile.go:251
+#: lxc/profile.go:253
 msgid "(none)"
 msgstr ""
 
-#: lxc/image.go:604 lxc/image.go:633
+#: lxc/image.go:624 lxc/image.go:666
 msgid "ALIAS"
 msgstr ""
 
-#: lxc/image.go:608
+#: lxc/image.go:628
 msgid "ARCH"
 msgstr ""
 
-#: lxc/list.go:378
+#: lxc/list.go:409
 msgid "ARCHITECTURE"
 msgstr ""
 
 <at>  <at>  -109,12 +109,12  <at>  <at>  msgstr ""
 msgid "Accept certificate"
 msgstr "証明書のフィンガープリントの確認なしで証明書を受け入れます"
 
-#: lxc/remote.go:256
+#: lxc/remote.go:269
 #, c-format
 msgid "Admin password for %s: "
 msgstr "%s の管理者パスワード: "
 
-#: lxc/image.go:347
+#: lxc/image.go:356
 msgid "Aliases:"
 msgstr "エイリアス:"
 
 <at>  <at>  -122,12 +122,12  <at>  <at>  msgstr "エイリアス:"
 msgid "An environment variable of the form HOME=/home/foo"
 msgstr "環境変数を HOME=/home/foo の形式で指定します"
 
-#: lxc/image.go:330 lxc/info.go:90
+#: lxc/image.go:339 lxc/info.go:90
 #, c-format
 msgid "Architecture: %s"
 msgstr "アーキテクチャ: %s"
 
-#: lxc/image.go:351
+#: lxc/image.go:360
 #, c-format
 msgid "Auto update: %s"
 msgstr "自動更新: %s"
 <at>  <at>  -144,11 +144,11  <at>  <at>  msgstr "受信バイト数"
 msgid "Bytes sent"
 msgstr "送信バイト数"
 
-#: lxc/config.go:270
+#: lxc/config.go:274
 msgid "COMMON NAME"
 msgstr ""
 
-#: lxc/list.go:379
+#: lxc/list.go:410
 msgid "CREATED AT"
 msgstr ""
 
 <at>  <at>  -162,11 +162,11  <at>  <at>  msgstr "標準入力から読み込めません: %s"
 msgid "Can't unset key '%s', it's not currently set."
 msgstr "キー '%s' が指定されていないので削除できません。"
 
-#: lxc/profile.go:417
+#: lxc/profile.go:419
 msgid "Cannot provide container name to list"
 msgstr "コンテナ名を取得できません"
 
-#: lxc/remote.go:206
+#: lxc/remote.go:219
 #, c-format
 msgid "Certificate fingerprint: %x"
 msgstr "証明書のフィンガープリント: %x"
 <at>  <at>  -182,11 +182,11  <at>  <at>  msgstr ""
 "\n"
 "lxc %s <name> [<name>...]"
 
-#: lxc/remote.go:279
+#: lxc/remote.go:292
 msgid "Client certificate stored at server: "
 msgstr "クライアント証明書がサーバに格納されました: "
 
-#: lxc/list.go:99 lxc/list.go:100
+#: lxc/list.go:121 lxc/list.go:122
 msgid "Columns"
 msgstr "カラムレイアウト"
 
 <at>  <at>  -194,12 +194,12  <at>  <at>  msgstr "カラムレイアウト"
 msgid "Config key/value to apply to the new container"
 msgstr "新しいコンテナに適用するキー/値の設定"
 
-#: lxc/config.go:500 lxc/config.go:565 lxc/image.go:687 lxc/profile.go:215
+#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:720 lxc/profile.go:217
 #, c-format
 msgid "Config parsing error: %s"
 msgstr "設定の構文エラー: %s"
 
-#: lxc/main.go:37
+#: lxc/main.go:29
 msgid "Connection refused; is LXD running?"
 msgstr "接続が拒否されました。LXDが実行されていますか?"
 
 <at>  <at>  -217,7 +217,7  <at>  <at>  msgstr "コンテナ名: %s"
 msgid "Container published with fingerprint: %s"
 msgstr "コンテナは以下のフィンガープリントで publish されます: %s"
 
-#: lxc/image.go:155
+#: lxc/image.go:157
 msgid "Copy aliases from source"
 msgstr "ソースからエイリアスをコピーしました"
 
 <at>  <at>  -233,12 +233,12  <at>  <at>  msgstr ""
 "lxc copy [remote:]<source container> [remote:]<destination container> [--"
 "ephemeral|e]"
 
-#: lxc/image.go:268
+#: lxc/image.go:271
 #, c-format
 msgid "Copying the image: %s"
 msgstr "イメージのコピー中: %s"
 
-#: lxc/remote.go:221
+#: lxc/remote.go:234
 msgid "Could not create server cert dir"
 msgstr "サーバ証明書格納用のディレクトリを作成できません。"
 
 <at>  <at>  -274,7 +274,7  <at>  <at>  msgstr ""
 "例:\n"
 "lxc snapshot u1 snap0"
 
-#: lxc/image.go:335 lxc/info.go:92
+#: lxc/image.go:344 lxc/info.go:92
 #, c-format
 msgid "Created: %s"
 msgstr "作成日時: %s"
 <at>  <at>  -288,7 +288,7  <at>  <at>  msgstr "%s を作成中"
 msgid "Creating the container"
 msgstr "コンテナを作成中"
 
-#: lxc/image.go:607 lxc/image.go:635
+#: lxc/image.go:627 lxc/image.go:668
 msgid "DESCRIPTION"
 msgstr ""
 
 <at>  <at>  -310,29 +310,29  <at>  <at>  msgstr ""
 "付属するデータ (設定、スナップショット、...) と一緒にコンテナもしくはコンテ\n"
 "ナのスナップショットを消去します。"
 
-#: lxc/config.go:617
+#: lxc/config.go:648
 #, c-format
 msgid "Device %s added to %s"
 msgstr "デバイス %s が %s に追加されました"
 
-#: lxc/config.go:804
+#: lxc/config.go:835
 #, c-format
 msgid "Device %s removed from %s"
 msgstr "デバイス %s が %s から削除されました"
 
-#: lxc/list.go:462
+#: lxc/list.go:554
 msgid "EPHEMERAL"
 msgstr ""
 
-#: lxc/config.go:272
+#: lxc/config.go:276
 msgid "EXPIRY DATE"
 msgstr ""
 
-#: lxc/main.go:55
+#: lxc/main.go:41
 msgid "Enables debug mode."
 msgstr "デバッグモードを有効にします。"
 
-#: lxc/main.go:54
+#: lxc/main.go:40
 msgid "Enables verbose mode."
 msgstr "詳細モードを有効にします。"
 
 <at>  <at>  -367,24 +367,24  <at>  <at>  msgstr ""
 "デフォルトのモードは non-interactive です。もし標準入出力が両方ともターミナ\n"
 "ルの場合は interactive モードが選択されます (標準エラー出力は無視されます)。"
 
-#: lxc/image.go:339
+#: lxc/image.go:348
 #, c-format
 msgid "Expires: %s"
 msgstr "失効日時: %s"
 
-#: lxc/image.go:341
+#: lxc/image.go:350
 msgid "Expires: never"
 msgstr "失効日時: 失効しない"
 
-#: lxc/config.go:269 lxc/image.go:605 lxc/image.go:634
+#: lxc/config.go:273 lxc/image.go:625 lxc/image.go:667
 msgid "FINGERPRINT"
 msgstr ""
 
-#: lxc/list.go:102
+#: lxc/list.go:124
 msgid "Fast mode (same as --columns=nsacPt"
 msgstr "Fast モード (--columns=nsacPt と同じ)"
 
-#: lxc/image.go:328
+#: lxc/image.go:337
 #, c-format
 msgid "Fingerprint: %s"
 msgstr "証明書のフィンガープリント: %s"
 <at>  <at>  -399,7 +399,7  <at>  <at>  msgstr ""
 "\n"
 "lxc finger <remote>"
 
-#: lxc/action.go:37
+#: lxc/action.go:37 lxc/action.go:38
 msgid "Force the container to shutdown."
 msgstr "コンテナを強制シャットダウンします。"
 
 <at>  <at>  -407,48 +407,48  <at>  <at>  msgstr "コンテナを強制シャットダウンします。"
 msgid "Force the removal of stopped containers."
 msgstr "停止したコンテナを強制的に削除します。"
 
-#: lxc/main.go:56
+#: lxc/main.go:42
 msgid "Force using the local unix socket."
 msgstr "強制的にローカルのUNIXソケットを使います。"
 
-#: lxc/list.go:101
+#: lxc/image.go:160 lxc/list.go:123
 msgid "Format"
 msgstr ""
 
-#: lxc/main.go:138
+#: lxc/remote.go:67
 msgid "Generating a client certificate. This may take a minute..."
 msgstr "クライアント証明書を生成します。1分ぐらいかかります..."
 
-#: lxc/list.go:376
+#: lxc/list.go:407
 msgid "IPV4"
 msgstr ""
 
-#: lxc/list.go:377
+#: lxc/list.go:408
 msgid "IPV6"
 msgstr ""
 
-#: lxc/config.go:271
+#: lxc/config.go:275
 msgid "ISSUE DATE"
 msgstr ""
 
-#: lxc/main.go:146
+#: lxc/main.go:132
 msgid ""
 "If this is your first time using LXD, you should also run: sudo lxd init"
 msgstr "初めて LXD を使う場合、sudo lxd init と実行する必要があります"
 
-#: lxc/main.go:57
+#: lxc/main.go:43
 msgid "Ignore aliases when determining what command to run."
 msgstr "どのコマンドを実行するか決める際にエイリアスを無視します。"
 
-#: lxc/action.go:40
+#: lxc/action.go:41
 msgid "Ignore the container state (only for start)."
 msgstr "コンテナの状態を無視します (startのみ)。"
 
-#: lxc/image.go:273
+#: lxc/image.go:276
 msgid "Image copied successfully!"
 msgstr "イメージのコピーが成功しました!"
 
-#: lxc/image.go:419
+#: lxc/image.go:428
 #, c-format
 msgid "Image imported with fingerprint: %s"
 msgstr "イメージは以下のフィンガープリントでインポートされました: %s"
 <at>  <at>  -481,16 +481,21  <at>  <at>  msgstr ""
 "例:\n"
 "lxc init ubuntu u1"
 
-#: lxc/remote.go:122
+#: lxc/remote.go:137
 #, c-format
 msgid "Invalid URL scheme \"%s\" in \"%s\""
 msgstr "不正な URL スキーム \"%s\" (\"%s\" 内)"
 
+#: lxc/config.go:254
+#, fuzzy
+msgid "Invalid certificate"
+msgstr "証明書のフィンガープリントの確認なしで証明書を受け入れます"
+
 #: lxc/init.go:30 lxc/init.go:35
 msgid "Invalid configuration key"
 msgstr "正しくない設定項目 (key) です"
 
-#: lxc/file.go:190
+#: lxc/file.go:195
 #, c-format
 msgid "Invalid source %s"
 msgstr "不正なソース %s"
 <at>  <at>  -504,11 +509,15  <at>  <at>  msgstr "不正な送り先 %s"
 msgid "Ips:"
 msgstr "IPアドレス:"
 
-#: lxc/image.go:156
+#: lxc/image.go:158
 msgid "Keep the image up to date after initial copy"
 msgstr "最初にコピーした後も常にイメージを最新の状態に保つ"
 
-#: lxc/main.go:35
+#: lxc/list.go:411
+msgid "LAST USED AT"
+msgstr ""
+
+#: lxc/main.go:27
 msgid "LXD socket not found; is LXD installed and running?"
 msgstr "LXD のソケットが見つかりません。LXD が実行されていますか?"
 
 <at>  <at>  -529,7 +538,8  <at>  <at>  msgid ""
 msgstr ""
 "指定したイメージからコンテナを起動します。\n"
 "\n"
-"lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]\n"
+"lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p "
+"<profile>...] [--config|-c <key=value>...]\n"
 "\n"
 "指定したイメージと名前を使ってコンテナを起動します。\n"
 "\n"
 <at>  <at>  -557,7 +567,8  <at>  <at>  msgstr ""
 "サーバ情報:\n"
 " lxc info [<remote>:]"
 
-#: lxc/list.go:67
+#: lxc/list.go:68
+#, fuzzy
 msgid ""
 "Lists the available resources.\n"
 "\n"
 <at>  <at>  -577,11 +588,18  <at>  <at>  msgid ""
 "* A regular expression matching a configuration item or its value. (e.g. "
 "volatile.eth0.hwaddr=00:16:3e:.*)\n"
 "\n"
-"Columns for table format are:\n"
+"The -c option takes a comma separated list of arguments that control\n"
+"which container attributes to output when displaying in table format.\n"
+"Column arguments are either pre-defined shorthand chars (see below),\n"
+"or (extended) config keys.  Commas between consecutive shorthand chars\n"
+"are optional.\n"
+"\n"
+"Pre-defined shorthand chars:\n"
 "* 4 - IPv4 address\n"
 "* 6 - IPv6 address\n"
 "* a - architecture\n"
 "* c - creation date\n"
+"* l - last used date\n"
 "* n - name\n"
 "* p - pid of container init process\n"
 "* P - profiles\n"
 <at>  <at>  -589,25 +607,43  <at>  <at>  msgid ""
 "* S - number of snapshots\n"
 "* t - type (persistent or ephemeral)\n"
 "\n"
+"Config key syntax: key[:name][:maxWidth]\n"
+"* key      - The (extended) config key to display\n"
+"* name     - Name to display in the column header, defaults to the key\n"
+"             if not specified or if empty (to allow defining maxWidth\n"
+"             without a custom name, e.g. user.key::0)\n"
+"* maxWidth - Max width of the column (longer results are truncated).\n"
+"             -1 == unlimited\n"
+"              0 == width of column header\n"
+"             >0 == max width in chars\n"
+"             Default is -1 (unlimited)\n"
+"\n"
 "Default column layout: ns46tS\n"
-"Fast column layout: nsacPt"
+"Fast column layout: nsacPt\n"
+"\n"
+"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile."
+"eth0.hwaddr:MAC\n"
 msgstr ""
 "利用可能なリソースを一覧表示します。\n"
 "\n"
 "lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n"
 "\n"
 "フィルタの指定:\n"
-"* 単一の \"web\" のようなキーワードを指定すると、名前が \"web\" ではじまるコンテ\n"
+"* 単一の \"web\" のようなキーワードを指定すると、名前が \"web\" ではじまるコ"
+"ンテ\n"
 "  ナが一覧表示されます。\n"
 "* コンテナ名の正規表現 (例: .*web.*01$)\n"
-"* 設定項目のキーと値。キーの名前空間は一意に識別できる場合は短縮することがで\n"
+"* 設定項目のキーと値。キーの名前空間は一意に識別できる場合は短縮することが"
+"で\n"
 "  きます:\n"
-" * \"user.blah=abc\" は \"blah\" という user プロパティが \"abc\" に設定されている\n"
+" * \"user.blah=abc\" は \"blah\" という user プロパティが \"abc\" に設定され"
+"ている\n"
 "  コンテナをすべて一覧表示します。\n"
 " * \"u.blah=abc\" は上記と同じ意味になります。\n"
 " * \"security.privileged=1\" は特権コンテナをすべて一覧表示します。\n"
 " * \"s.privilaged=1\" は上記と同じ意味になります。\n"
-" * 設定項目もしくは値とマッチする正規表現 (例:volatile.eth0.hwaddr=00:16:3e:.*)\n"
+" * 設定項目もしくは値とマッチする正規表現 (例:volatile.eth0.hwaddr=00:16:3e:."
+"*)\n"
 "\n"
 "表のカラムの指定:\n"
 "* 4 - IPv4 アドレス\n"
 <at>  <at>  -628,7 +664,7  <at>  <at>  msgstr ""
 msgid "Log:"
 msgstr "ログ:"
 
-#: lxc/image.go:154
+#: lxc/image.go:156
 msgid "Make image public"
 msgstr "イメージを public にする"
 
 <at>  <at>  -637,6 +673,7  <at>  <at>  msgid "Make the image public"
 msgstr "イメージを public にする"
 
 #: lxc/profile.go:48
+#, fuzzy
 msgid ""
 "Manage configuration profiles.\n"
 "\n"
 <at>  <at>  -654,16 +691,21  <at>  <at>  msgid ""
 "             cat profile.yml | lxc profile edit <profile> # read from "
 "profile.yml\n"
 "\n"
-"lxc profile apply <container> <profiles>\n"
-"    Apply a comma-separated list of profiles to a container, in order.\n"
+"lxc profile assign <container> <profiles>\n"
+"    Assign a comma-separated list of profiles to a container, in order.\n"
 "    All profiles passed in this call (and only those) will be applied\n"
-"    to the specified container.\n"
-"    Example: lxc profile apply foo default,bar # Apply default and bar\n"
-"             lxc profile apply foo default # Only default is active\n"
-"             lxc profile apply '' # no profiles are applied anymore\n"
-"             lxc profile apply bar,default # Apply default second now\n"
-"lxc profile apply-add <container> <profile>\n"
-"lxc profile apply-remove <container> <profile>\n"
+"    to the specified container, i.e. it sets the list of profiles exactly "
+"to\n"
+"    those specified in this command. To add/remove a particular profile from "
+"a\n"
+"    container, use {add|remove} below.\n"
+"    Example: lxc profile assign foo default,bar # Apply default and bar\n"
+"             lxc profile assign foo default # Only default is active\n"
+"             lxc profile assign '' # no profiles are applied anymore\n"
+"             lxc profile assign bar,default # Apply default second now\n"
+"lxc profile add <container> <profile> # add a profile to a container\n"
+"lxc profile remove <container> <profile> # remove the profile from a "
+"container\n"
 "\n"
 "Devices:\n"
 "lxc profile device list <profile>                                   List "
 <at>  <at>  -702,7 +744,8  <at>  <at>  msgstr ""
 "lxc profile edit <profile>\n"
 "    プロファイルを編集します。外部エディタもしくはSTDINから読み込みます。\n"
 "    例: lxc profile edit <profile> # エディタの起動\n"
-"        cat profile.yml | lxc profile edit <profile> # profile.yml から読み込み\n"
+"        cat profile.yml | lxc profile edit <profile> # profile.yml から読み込"
+"み\n"
 "\n"
 "lxc profile apply <container> <profiles>\n"
 "    プロファイルのコンマ区切りのリストをコンテナに順番に適用します。\n"
 <at>  <at>  -727,7 +770,8  <at>  <at>  msgstr ""
 "    デバイスプロパティを設定します\n"
 "lxc profile device unset <[remote:]profile> <name> <key>\n"
 "    デバイスプロパティを削除します\n"
-"lxc profile device add <profile name> <device name> <device type> [key=value]...\n"
+"lxc profile device add <profile name> <device name> <device type> "
+"[key=value]...\n"
 "    ディスクやNICのようなプロファイルデバイスを指定したプロファイルを使って\n"
 "    コンテナに追加します。"
 
 <at>  <at>  -905,7 +949,8  <at>  <at>  msgstr ""
 "lxc remote get-default\n"
 "    デフォルトに設定されているリモートホストを表示します。"
 
-#: lxc/image.go:93
+#: lxc/image.go:95
+#, fuzzy
 msgid ""
 "Manipulate container images.\n"
 "\n"
 <at>  <at>  -936,8 +981,8  <at>  <at>  msgid ""
 "    The auto-update flag instructs the server to keep this image up to\n"
 "    date. It requires the source to be an alias and for it to be public.\n"
 "\n"
-"lxc image delete [remote:]<image>\n"
-"    Delete an image from the LXD image store.\n"
+"lxc image delete [remote:]<image> [remote:][<image>...]\n"
+"    Delete one or more images from the LXD image store.\n"
 "\n"
 "lxc image export [remote:]<image>\n"
 "    Export an image from the LXD image store into a distributable tarball.\n"
 <at>  <at>  -945,7 +990,7  <at>  <at>  msgid ""
 "lxc image info [remote:]<image>\n"
 "    Print everything LXD knows about a given image.\n"
 "\n"
-"lxc image list [remote:] [filter]\n"
+"lxc image list [remote:] [filter] [--format table|json]\n"
 "    List images in the LXD image store. Filters may be of the\n"
 "    <key>=<value> form for property based filtering, or part of the image\n"
 "    hash or part of the image alias name.\n"
 <at>  <at>  -983,11 +1028,14  <at>  <at>  msgstr ""
 "る場合は) エイリアスで参照できます。\n"
 "\n"
 "\n"
-"lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS].. [prop=value]\n"
+"lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--"
+"created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--"
+"alias=ALIAS].. [prop=value]\n"
 "    イメージの tarball (複数も可能) を LXD のイメージストアにインポートしま\n"
 "    す。\n"
 "\n"
-"lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public] [--auto-update]\n"
+"lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] "
+"[--public] [--auto-update]\n"
 "    ネットワーク経由である LXD デーモンから他の LXD デーモンへイメージを\n"
 "    コピーします。\n"
 "\n"
 <at>  <at>  -1024,7 +1072,8  <at>  <at>  msgstr ""
 "    エイリアスを削除します。\n"
 "\n"
 "lxc image alias list [remote:] [filter]\n"
-"    エイリアスを一覧表示します。イメージハッシュの一部やイメージのエイリアス\n"
+"    エイリアスを一覧表示します。イメージハッシュの一部やイメージのエイリア"
+"ス\n"
 "    名の一部をフィルタとして指定できます。\n"
 
 #: lxc/info.go:147
 <at>  <at>  -1065,7 +1114,7  <at>  <at>  msgstr ""
 "例:\n"
 "lxc monitor --type=logging"
 
-#: lxc/file.go:178
+#: lxc/file.go:183
 msgid "More than one file to download, but target is not a directory"
 msgstr ""
 "ダウンロード対象のファイルが複数ありますが、コピー先がディレクトリではありま"
 <at>  <at>  -1091,15 +1140,15  <at>  <at>  msgstr ""
 "lxc move <old name> <new name>\n"
 "    ローカルのコンテナをリネームします。\n"
 
-#: lxc/action.go:63
+#: lxc/action.go:64
 msgid "Must supply container name for: "
 msgstr "コンテナ名を指定する必要があります: "
 
-#: lxc/list.go:380 lxc/remote.go:363
+#: lxc/list.go:412 lxc/remote.go:376
 msgid "NAME"
 msgstr ""
 
-#: lxc/remote.go:337 lxc/remote.go:342
+#: lxc/remote.go:350 lxc/remote.go:355
 msgid "NO"
 msgstr ""
 
 <at>  <at>  -1108,31 +1157,31  <at>  <at>  msgstr ""
 msgid "Name: %s"
 msgstr "コンテナ名: %s"
 
-#: lxc/image.go:157 lxc/publish.go:33
+#: lxc/image.go:159 lxc/publish.go:33
 msgid "New alias to define at target"
 msgstr "新しいエイリアスを定義する"
 
-#: lxc/config.go:281
+#: lxc/config.go:285
 msgid "No certificate provided to add"
 msgstr "追加すべき証明書が提供されていません"
 
-#: lxc/config.go:304
+#: lxc/config.go:308
 msgid "No fingerprint specified."
 msgstr "フィンガープリントが指定されていません。"
 
-#: lxc/remote.go:107
+#: lxc/remote.go:122
 msgid "Only https URLs are supported for simplestreams"
 msgstr "simplestreams は https の URL のみサポートします"
 
-#: lxc/image.go:411
+#: lxc/image.go:420
 msgid "Only https:// is supported for remote image import."
 msgstr "リモートイメージのインポートは https:// のみをサポートします。"
 
-#: lxc/help.go:63 lxc/main.go:122
+#: lxc/help.go:63 lxc/main.go:108
 msgid "Options:"
 msgstr "オプション:"
 
-#: lxc/image.go:506
+#: lxc/image.go:524
 #, c-format
 msgid "Output is in %s"
 msgstr "%s に出力されます"
 <at>  <at>  -1141,23 +1190,23  <at>  <at>  msgstr "%s に出力されます"
 msgid "Override the terminal mode (auto, interactive or non-interactive)"
 msgstr "ターミナルモードを上書きします (auto, interactive, non-interactive)"
 
-#: lxc/list.go:464
+#: lxc/list.go:556
 msgid "PERSISTENT"
 msgstr ""
 
-#: lxc/list.go:381
+#: lxc/list.go:413
 msgid "PID"
 msgstr ""
 
-#: lxc/list.go:382
+#: lxc/list.go:414
 msgid "PROFILES"
 msgstr ""
 
-#: lxc/remote.go:365
+#: lxc/remote.go:378
 msgid "PROTOCOL"
 msgstr ""
 
-#: lxc/image.go:606 lxc/remote.go:366
+#: lxc/image.go:626 lxc/remote.go:379
 msgid "PUBLIC"
 msgstr ""
 
 <at>  <at>  -1177,7 +1226,7  <at>  <at>  msgstr "別のクライアント用設定ディレクトリ"
 msgid "Path to an alternate server directory."
 msgstr "別のサーバ用設定ディレクトリ"
 
-#: lxc/main.go:39
+#: lxc/main.go:31
 msgid "Permisson denied, are you in the lxd group?"
 msgstr "アクセスが拒否されました。lxd グループに所属していますか?"
 
 <at>  <at>  -1196,11 +1245,11  <at>  <at>  msgstr ""
 "\n"
 "lxd help [--all]"
 
-#: lxc/profile.go:216
+#: lxc/profile.go:218
 msgid "Press enter to open the editor again"
 msgstr "再度エディタを開くためには Enter キーを押します"
 
-#: lxc/config.go:501 lxc/config.go:566 lxc/image.go:688
+#: lxc/config.go:532 lxc/config.go:597 lxc/image.go:721
 msgid "Press enter to start the editor again"
 msgstr "再度エディタを起動するには Enter キーを押します"
 
 <at>  <at>  -1231,22 +1280,22  <at>  <at>  msgstr ""
 msgid "Processes: %d"
 msgstr "プロセス数: %d"
 
-#: lxc/profile.go:272
+#: lxc/profile.go:274
 #, c-format
 msgid "Profile %s added to %s"
 msgstr "プロファイル %s が %s に追加されました"
 
-#: lxc/profile.go:167
+#: lxc/profile.go:169
 #, c-format
 msgid "Profile %s created"
 msgstr "プロファイル %s を作成しました"
 
-#: lxc/profile.go:237
+#: lxc/profile.go:239
 #, c-format
 msgid "Profile %s deleted"
 msgstr "プロファイル %s を削除しました"
 
-#: lxc/profile.go:303
+#: lxc/profile.go:305
 #, c-format
 msgid "Profile %s removed from %s"
 msgstr "プロファイル %s が %s から削除されました"
 <at>  <at>  -1255,7 +1304,7  <at>  <at>  msgstr "プロファイル %s が %s から削除されました"
 msgid "Profile to apply to the new container"
 msgstr "新しいコンテナに適用するプロファイル"
 
-#: lxc/profile.go:253
+#: lxc/profile.go:255
 #, c-format
 msgid "Profiles %s applied to %s"
 msgstr "プロファイル %s が %s に追加されました"
 <at>  <at>  -1265,7 +1314,7  <at>  <at>  msgstr "プロファイル %s が %s に追加されました"
 msgid "Profiles: %s"
 msgstr "プロファイル: %s"
 
-#: lxc/image.go:343
+#: lxc/image.go:352
 msgid "Properties:"
 msgstr "プロパティ:"
 
 <at>  <at>  -1273,7 +1322,7  <at>  <at>  msgstr "プロパティ:"
 msgid "Public image server"
 msgstr "Public なイメージサーバとして設定します"
 
-#: lxc/image.go:331
+#: lxc/image.go:340
 #, c-format
 msgid "Public: %s"
 msgstr ""
 <at>  <at>  -1312,27 +1361,27  <at>  <at>  msgstr "リソース:"
 msgid "Retrieving image: %s"
 msgstr "イメージの取得中: %s"
 
-#: lxc/image.go:609
+#: lxc/image.go:629
 msgid "SIZE"
 msgstr ""
 
-#: lxc/list.go:383
+#: lxc/list.go:415
 msgid "SNAPSHOTS"
 msgstr ""
 
-#: lxc/list.go:384
+#: lxc/list.go:416
 msgid "STATE"
 msgstr ""
 
-#: lxc/remote.go:367
+#: lxc/remote.go:380
 msgid "STATIC"
 msgstr ""
 
-#: lxc/remote.go:214
+#: lxc/remote.go:227
 msgid "Server certificate NACKed by user"
 msgstr "ユーザによりサーバ証明書が拒否されました"
 
-#: lxc/remote.go:276
+#: lxc/remote.go:289
 msgid "Server doesn't trust us after adding our cert"
 msgstr "サーバが我々の証明書を追加した後我々を信頼していません"
 
 <at>  <at>  -1384,7 +1433,7  <at>  <at>  msgstr "全てコマンドを表示します (主なコマンドだけではな
 msgid "Show the container's last 100 log lines?"
 msgstr "コンテナログの最後の 100 行を表示しますか?"
 
-#: lxc/image.go:329
+#: lxc/image.go:338
 #, c-format
 msgid "Size: %.2fMB"
 msgstr "サイズ: %.2fMB"
 <at>  <at>  -1393,7 +1442,7  <at>  <at>  msgstr "サイズ: %.2fMB"
 msgid "Snapshots:"
 msgstr "スナップショット:"
 
-#: lxc/image.go:353
+#: lxc/image.go:362
 msgid "Source:"
 msgstr "取得元:"
 
 <at>  <at>  -1415,7 +1464,7  <at>  <at>  msgstr "実行中の場合、コンテナを停止します"
 msgid "Stopping container failed!"
 msgstr "コンテナの停止に失敗しました!"
 
-#: lxc/action.go:39
+#: lxc/action.go:40
 msgid "Store the container state (only for stop)."
 msgstr "コンテナの状態を保存します (stopのみ)。"
 
 <at>  <at>  -1427,7 +1476,7  <at>  <at>  msgstr "Swap (現在値)"
 msgid "Swap (peak)"
 msgstr "Swap (ピーク)"
 
-#: lxc/list.go:385
+#: lxc/list.go:417
 msgid "TYPE"
 msgstr ""
 
 <at>  <at>  -1443,15 +1492,16  <at>  <at>  msgstr ""
 "コンテナは現在実行中です。停止して、再起動するために --force を使用してくだ\n"
 "さい。"
 
-#: lxc/config.go:645 lxc/config.go:657 lxc/config.go:690 lxc/config.go:708
-#: lxc/config.go:746 lxc/config.go:764
+#: lxc/config.go:676 lxc/config.go:688 lxc/config.go:721 lxc/config.go:739
+#: lxc/config.go:777 lxc/config.go:795
 msgid "The device doesn't exist"
 msgstr "デバイスが存在しません"
 
 #: lxc/init.go:277
 #, c-format
 msgid "The local image '%s' couldn't be found, trying '%s:' instead."
-msgstr "ローカルイメージ '%s' が見つかりません。代わりに '%s:' を試してみてください。"
+msgstr ""
+"ローカルイメージ '%s' が見つかりません。代わりに '%s:' を試してみてください。"
 
 #: lxc/publish.go:62
 msgid "There is no \"image name\".  Did you want an alias?"
 <at>  <at>  -1464,22 +1514,23  <at>  <at>  msgstr ""
 msgid "Time to wait for the container before killing it."
 msgstr "コンテナを強制停止するまでの時間"
 
-#: lxc/image.go:332
+#: lxc/image.go:341
 msgid "Timestamps:"
 msgstr "タイムスタンプ:"
 
-#: lxc/main.go:147
+#: lxc/main.go:133
 msgid "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr ""
-"初めてコンテナを起動するには、\"lxc launch ubuntu:16.04\" と実行してみてくだ\n"
+"初めてコンテナを起動するには、\"lxc launch ubuntu:16.04\" と実行してみてく"
+"だ\n"
 "さい。"
 
-#: lxc/image.go:402
+#: lxc/image.go:411
 #, c-format
 msgid "Transferring image: %d%%"
 msgstr "イメージを転送中: %d%%"
 
-#: lxc/action.go:93 lxc/launch.go:132
+#: lxc/action.go:94 lxc/launch.go:132
 #, c-format
 msgid "Try `lxc info --show-log %s` for more info"
 msgstr "更に情報を得るために `lxc info --show-log` を実行してみてください"
 <at>  <at>  -1492,24 +1543,24  <at>  <at>  msgstr "タイプ: ephemeral"
 msgid "Type: persistent"
 msgstr "タイプ: persistent"
 
-#: lxc/image.go:610
+#: lxc/image.go:630
 msgid "UPLOAD DATE"
 msgstr ""
 
-#: lxc/remote.go:364
+#: lxc/remote.go:377
 msgid "URL"
 msgstr ""
 
-#: lxc/remote.go:82
+#: lxc/remote.go:97
 msgid "Unable to read remote TLS certificate"
 msgstr "リモートの TLS 証明書を読めません"
 
-#: lxc/image.go:337
+#: lxc/image.go:346
 #, c-format
 msgid "Uploaded: %s"
 msgstr "アップロード日時: %s"
 
-#: lxc/main.go:122
+#: lxc/main.go:108
 #, c-format
 msgid "Usage: %s"
 msgstr "使い方: %s"
 <at>  <at>  -1537,11 +1588,11  <at>  <at>  msgstr "コンテナの稼動状態のスナップショットを取得するか
 msgid "Whether to show the expanded configuration"
 msgstr "拡張した設定を表示するかどうか"
 
-#: lxc/remote.go:339 lxc/remote.go:344
+#: lxc/remote.go:352 lxc/remote.go:357
 msgid "YES"
 msgstr ""
 
-#: lxc/main.go:66
+#: lxc/main.go:52
 msgid "`lxc config profile` is deprecated, please use `lxc profile`"
 msgstr "`lxc config profile` は廃止されました。`lxc profile` を使ってください"
 
 <at>  <at>  -1550,19 +1601,19  <at>  <at>  msgid "bad number of things scanned from image, container or snapshot"
 msgstr ""
 "イメージ、コンテナ、スナップショットのいずれかからスキャンされた数が不正"
 
-#: lxc/action.go:89
+#: lxc/action.go:90
 msgid "bad result type from action"
 msgstr "アクションからの結果タイプが不正です"
 
-#: lxc/copy.go:78
+#: lxc/copy.go:99
 msgid "can't copy to the same container name"
 msgstr "同じコンテナ名へはコピーできません"
 
-#: lxc/remote.go:327
+#: lxc/remote.go:340
 msgid "can't remove the default remote"
 msgstr "デフォルトのリモートは削除できません"
 
-#: lxc/remote.go:353
+#: lxc/remote.go:366
 msgid "default"
 msgstr ""
 
 <at>  <at>  -1572,20 +1623,20  <at>  <at>  msgstr ""
 "サーバから変更されたイメージ、コンテナ、スナップショットを取得できませんで\n"
 "した"
 
-#: lxc/image.go:323
+#: lxc/image.go:332
 msgid "disabled"
 msgstr "無効"
 
-#: lxc/image.go:325
+#: lxc/image.go:334
 msgid "enabled"
 msgstr "有効"
 
-#: lxc/main.go:25 lxc/main.go:159
+#: lxc/main.go:22 lxc/main.go:144
 #, c-format
 msgid "error: %v"
 msgstr "エラー: %v"
 
-#: lxc/help.go:40 lxc/main.go:117
+#: lxc/help.go:40 lxc/main.go:103
 #, c-format
 msgid "error: unknown command: %s"
 msgstr "エラー: 未知のコマンド: %s"
 <at>  <at>  -1594,39 +1645,39  <at>  <at>  msgstr "エラー: 未知のコマンド: %s"
 msgid "got bad version"
 msgstr "不正なバージョンを得ました"
 
-#: lxc/image.go:318 lxc/image.go:586
+#: lxc/image.go:327 lxc/image.go:606
 msgid "no"
 msgstr ""
 
-#: lxc/copy.go:101
+#: lxc/copy.go:122
 msgid "not all the profiles from the source exist on the target"
 msgstr "コピー元の全てのプロファイルがターゲットに存在しません"
 
-#: lxc/remote.go:207
+#: lxc/remote.go:220
 msgid "ok (y/n)?"
 msgstr "ok (y/n)?"
 
-#: lxc/main.go:266 lxc/main.go:270
+#: lxc/main.go:277 lxc/main.go:281
 #, c-format
 msgid "processing aliases failed %s\n"
 msgstr "エイリアスの処理が失敗しました %s\n"
 
-#: lxc/remote.go:389
+#: lxc/remote.go:402
 #, c-format
 msgid "remote %s already exists"
 msgstr "リモート %s は既に存在します"
 
-#: lxc/remote.go:319 lxc/remote.go:381 lxc/remote.go:416 lxc/remote.go:432
+#: lxc/remote.go:332 lxc/remote.go:394 lxc/remote.go:429 lxc/remote.go:445
 #, c-format
 msgid "remote %s doesn't exist"
 msgstr "リモート %s は存在しません"
 
-#: lxc/remote.go:302
+#: lxc/remote.go:315
 #, c-format
 msgid "remote %s exists as <%s>"
 msgstr "リモート %s は <%s> として存在します"
 
-#: lxc/remote.go:323 lxc/remote.go:385 lxc/remote.go:420
+#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433
 #, c-format
 msgid "remote %s is static and cannot be modified"
 msgstr "リモート %s は static ですので変更できません"
 <at>  <at>  -1648,11 +1699,11  <at>  <at>  msgstr "%s に取得しました"
 msgid "unreachable return reached"
 msgstr "到達しないはずのreturnに到達しました"
 
-#: lxc/main.go:199
+#: lxc/main.go:209
 msgid "wrong number of subcommand arguments"
 msgstr "サブコマンドの引数の数が正しくありません"
 
-#: lxc/delete.go:45 lxc/image.go:320 lxc/image.go:590
+#: lxc/delete.go:45 lxc/image.go:329 lxc/image.go:610
 msgid "yes"
 msgstr ""
 
diff --git a/po/lxd.pot b/po/lxd.pot
index 471930f..8322a94 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
 <at>  <at>  -7,7 +7,7  <at>  <at> 
 msgid   ""
 msgstr  "Project-Id-Version: lxd\n"
         "Report-Msgid-Bugs-To: lxc-devel <at> lists.linuxcontainers.org\n"
-        "POT-Creation-Date: 2016-06-16 15:51-0400\n"
+        "POT-Creation-Date: 2016-06-24 08:22-0700\n"
         "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
         "Last-Translator: FULL NAME <EMAIL <at> ADDRESS>\n"
         "Language-Team: LANGUAGE <LL <at> li.org>\n"
 <at>  <at>  -98,7 +98,7  <at>  <at>  msgstr  ""
 msgid   "ARCH"
 msgstr  ""
 
-#: lxc/list.go:379
+#: lxc/list.go:409
 msgid   "ARCHITECTURE"
 msgstr  ""
 
 <at>  <at>  -145,7 +145,7  <at>  <at>  msgstr  ""
 msgid   "COMMON NAME"
 msgstr  ""
 
-#: lxc/list.go:380
+#: lxc/list.go:410
 msgid   "CREATED AT"
 msgstr  ""
 
 <at>  <at>  -179,7 +179,7  <at>  <at>  msgstr  ""
 msgid   "Client certificate stored at server: "
 msgstr  ""
 
-#: lxc/list.go:100 lxc/list.go:101
+#: lxc/list.go:121 lxc/list.go:122
 msgid   "Columns"
 msgstr  ""
 
 <at>  <at>  -281,7 +281,7  <at>  <at>  msgstr  ""
 msgid   "Device %s removed from %s"
 msgstr  ""
 
-#: lxc/list.go:464
+#: lxc/list.go:554
 msgid   "EPHEMERAL"
 msgstr  ""
 
 <at>  <at>  -330,7 +330,7  <at>  <at>  msgstr  ""
 msgid   "FINGERPRINT"
 msgstr  ""
 
-#: lxc/list.go:103
+#: lxc/list.go:124
 msgid   "Fast mode (same as --columns=nsacPt"
 msgstr  ""
 
 <at>  <at>  -345,7 +345,7  <at>  <at>  msgid   "Fingers the LXD instance to check if it is up and working.\n"
         "lxc finger <remote>"
 msgstr  ""
 
-#: lxc/action.go:37
+#: lxc/action.go:37 lxc/action.go:38
 msgid   "Force the container to shutdown."
 msgstr  ""
 
 <at>  <at>  -357,7 +357,7  <at>  <at>  msgstr  ""
 msgid   "Force using the local unix socket."
 msgstr  ""
 
-#: lxc/image.go:160 lxc/list.go:102
+#: lxc/image.go:160 lxc/list.go:123
 msgid   "Format"
 msgstr  ""
 
 <at>  <at>  -365,11 +365,11  <at>  <at>  msgstr  ""
 msgid   "Generating a client certificate. This may take a minute..."
 msgstr  ""
 
-#: lxc/list.go:377
+#: lxc/list.go:407
 msgid   "IPV4"
 msgstr  ""
 
-#: lxc/list.go:378
+#: lxc/list.go:408
 msgid   "IPV6"
 msgstr  ""
 
 <at>  <at>  -385,7 +385,7  <at>  <at>  msgstr  ""
 msgid   "Ignore aliases when determining what command to run."
 msgstr  ""
 
-#: lxc/action.go:40
+#: lxc/action.go:41
 msgid   "Ignore the container state (only for start)."
 msgstr  ""
 
 <at>  <at>  -443,7 +443,7  <at>  <at>  msgstr  ""
 msgid   "Keep the image up to date after initial copy"
 msgstr  ""
 
-#: lxc/list.go:381
+#: lxc/list.go:411
 msgid   "LAST USED AT"
 msgstr  ""
 
 <at>  <at>  -475,7 +475,7  <at>  <at>  msgid   "List information on LXD servers and containers.\n"
         " lxc info [<remote>:]"
 msgstr  ""
 
-#: lxc/list.go:67
+#: lxc/list.go:68
 msgid   "Lists the available resources.\n"
         "\n"
         "lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n"
 <at>  <at>  -490,7 +490,13  <at>  <at>  msgid   "Lists the available resources.\n"
         " * \"s.privileged=1\" will do the same\n"
         "* A regular expression matching a configuration item or its value. (e.g. volatile.eth0.hwaddr=00:16:3e:.*)\n"
         "\n"
-        "Columns for table format are:\n"
+        "The -c option takes a comma separated list of arguments that control\n"
+        "which container attributes to output when displaying in table format.\n"
+        "Column arguments are either pre-defined shorthand chars (see below),\n"
+        "or (extended) config keys.  Commas between consecutive shorthand chars\n"
+        "are optional.\n"
+        "\n"
+        "Pre-defined shorthand chars:\n"
         "* 4 - IPv4 address\n"
         "* 6 - IPv6 address\n"
         "* a - architecture\n"
 <at>  <at>  -503,8 +509,21  <at>  <at>  msgid   "Lists the available resources.\n"
         "* S - number of snapshots\n"
         "* t - type (persistent or ephemeral)\n"
         "\n"
+        "Config key syntax: key[:name][:maxWidth]\n"
+        "* key      - The (extended) config key to display\n"
+        "* name     - Name to display in the column header, defaults to the key\n"
+        "             if not specified or if empty (to allow defining maxWidth\n"
+        "             without a custom name, e.g. user.key::0)\n"
+        "* maxWidth - Max width of the column (longer results are truncated).\n"
+        "             -1 == unlimited\n"
+        "              0 == width of column header\n"
+        "             >0 == max width in chars\n"
+        "             Default is -1 (unlimited)\n"
+        "\n"
         "Default column layout: ns46tS\n"
-        "Fast column layout: nsacPt"
+        "Fast column layout: nsacPt\n"
+        "\n"
+        "Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0.hwaddr:MAC\n"
 msgstr  ""
 
 #: lxc/info.go:225
 <at>  <at>  -719,11 +738,11  <at>  <at>  msgid   "Move containers within or in between lxd instances.\n"
         "    Rename a local container.\n"
 msgstr  ""
 
-#: lxc/action.go:63
+#: lxc/action.go:64
 msgid   "Must supply container name for: "
 msgstr  ""
 
-#: lxc/list.go:382 lxc/remote.go:376
+#: lxc/list.go:412 lxc/remote.go:376
 msgid   "NAME"
 msgstr  ""
 
 <at>  <at>  -769,15 +788,15  <at>  <at>  msgstr  ""
 msgid   "Override the terminal mode (auto, interactive or non-interactive)"
 msgstr  ""
 
-#: lxc/list.go:466
+#: lxc/list.go:556
 msgid   "PERSISTENT"
 msgstr  ""
 
-#: lxc/list.go:383
+#: lxc/list.go:413
 msgid   "PID"
 msgstr  ""
 
-#: lxc/list.go:384
+#: lxc/list.go:414
 msgid   "PROFILES"
 msgstr  ""
 
 <at>  <at>  -930,11 +949,11  <at>  <at>  msgstr  ""
 msgid   "SIZE"
 msgstr  ""
 
-#: lxc/list.go:385
+#: lxc/list.go:415
 msgid   "SNAPSHOTS"
 msgstr  ""
 
-#: lxc/list.go:386
+#: lxc/list.go:416
 msgid   "STATE"
 msgstr  ""
 
 <at>  <at>  -1018,7 +1037,7  <at>  <at>  msgstr  ""
 msgid   "Stopping container failed!"
 msgstr  ""
 
-#: lxc/action.go:39
+#: lxc/action.go:40
 msgid   "Store the container state (only for stop)."
 msgstr  ""
 
 <at>  <at>  -1030,7 +1049,7  <at>  <at>  msgstr  ""
 msgid   "Swap (peak)"
 msgstr  ""
 
-#: lxc/list.go:387
+#: lxc/list.go:417
 msgid   "TYPE"
 msgstr  ""
 
 <at>  <at>  -1072,7 +1091,7  <at>  <at>  msgstr  ""
 msgid   "Transferring image: %d%%"
 msgstr  ""
 
-#: lxc/action.go:93 lxc/launch.go:132
+#: lxc/action.go:94 lxc/launch.go:132
 #, c-format
 msgid   "Try `lxc info --show-log %s` for more info"
 msgstr  ""
 <at>  <at>  -1139,7 +1158,7  <at>  <at>  msgstr  ""
 msgid   "bad number of things scanned from image, container or snapshot"
 msgstr  ""
 
-#: lxc/action.go:89
+#: lxc/action.go:90
 msgid   "bad result type from action"
 msgstr  ""
 
diff --git a/shared/container.go b/shared/container.go
index 46fdb2f..bf997d1 100644
--- a/shared/container.go
+++ b/shared/container.go
 <at>  <at>  -1,6 +1,9  <at>  <at> 
 package shared
 
 import (
+	"fmt"
+	"strconv"
+	"strings"
 	"time"
 )
 
 <at>  <at>  -141,3 +144,116  <at>  <at>  type ProfileConfig struct {
 	Description string            `json:"description"`
 	Devices     Devices           `json:"devices"`
 }
+
+func isInt64(value string) error {
+	if value == "" {
+		return nil
+	}
+
+	_, err := strconv.ParseInt(value, 10, 64)
+	if err != nil {
+		return fmt.Errorf("Invalid value for an integer: %s", value)
+	}
+
+	return nil
+}
+
+func isBool(value string) error {
+	if value == "" {
+		return nil
+	}
+
+	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
+		return fmt.Errorf("Invalid value for a boolean: %s", value)
+	}
+
+	return nil
+}
+
+func isOneOf(value string, valid []string) error {
+	if value == "" {
+		return nil
+	}
+
+	if !StringInSlice(value, valid) {
+		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
+	}
+
+	return nil
+}
+
+func isAny(value string) error {
+	return nil
+}
+
+// KnownContainerConfigKeys maps all fully defined, well-known config keys
+// to an appropriate checker function, which validates whether or not a
+// given value is syntactically legal.
+var KnownContainerConfigKeys = map[string]func(value string) error{
+	"boot.autostart":             isBool,
+	"boot.autostart.delay":       isInt64,
+	"boot.autostart.priority":    isInt64,
+	"boot.host_shutdown_timeout": isInt64,
+
+	"limits.cpu":           isAny,
+	"limits.disk.priority": isInt64,
+	"limits.memory":        isAny,
+	"limits.memory.enforce": func(value string) error {
+		return isOneOf(value, []string{"soft", "hard"})
+	},
+	"limits.memory.swap":          isBool,
+	"limits.memory.swap.priority": isInt64,
+	"limits.network.priority":     isInt64,
+	"limits.processes":            isInt64,
+
+	"linux.kernel_modules": isAny,
+
+	"security.privileged":                 isBool,
+	"security.nesting":                    isBool,
+	"security.syscalls.blacklist_default": isBool,
+	"security.syscalls.blacklist_compat":  isBool,
+	"security.syscalls.blacklist":         isAny,
+	"security.syscalls.whitelist":         isAny,
+
+	// Caller is responsible for full validation of any raw.* value
+	"raw.apparmor": isAny,
+	"raw.lxc":      isAny,
+	"raw.seccomp":  isAny,
+
+	"volatile.apply_template":   isAny,
+	"volatile.base_image":       isAny,
+	"volatile.last_state.idmap": isAny,
+	"volatile.last_state.power": isAny,
+}
+
+// ConfigKeyChecker returns a function that will check whether or not
+// a provide value is valid for the associate config key.  Returns an
+// error if the key is not known.  The checker function only performs
+// syntactic checking of the value, semantic and usage checking must
+// be done by the caller.  User defined keys are always considered to
+// be valid, e.g. user.* and environment.* keys.
+func ConfigKeyChecker(key string) (func(value string) error, error) {
+	if f, ok := KnownContainerConfigKeys[key]; ok {
+		return f, nil
+	}
+
+	if strings.HasPrefix(key, "volatile.") {
+		if strings.HasSuffix(key, ".hwaddr") {
+			return isAny, nil
+		}
+
+		if strings.HasSuffix(key, ".name") {
+			return isAny, nil
+		}
+	}
+
+	if strings.HasPrefix(key, "environment.") {
+		return isAny, nil
+	}
+
+	if strings.HasPrefix(key, "user.") {
+		return isAny, nil
+	}
+
+	return nil, fmt.Errorf("Bad key: %s", key)
+}
diff --git a/shared/util.go b/shared/util.go
index ab6d061..b6539c2 100644
--- a/shared/util.go
+++ b/shared/util.go
 <at>  <at>  -704,6 +704,18  <at>  <at>  func GetByteSizeString(input int64) string {
 	return fmt.Sprintf("%.2fEB", value)
 }
 
+// RemoveDuplicatesFromString removes all duplicates of the string 'sep'
+// from the specified string 's'.  Leading and trailing occurences of sep
+// are NOT removed (duplicate leading/trailing are).  Performs poorly if
+// there are multiple consecutive redundant separators.
+func RemoveDuplicatesFromString(s string, sep string) string {
+	dup := sep + sep
+	for s = strings.Replace(s, dup, sep, -1); strings.Contains(s, dup); s = strings.Replace(s, dup, sep, -1) {
+
+	}
+	return s
+}
+
 type TransferProgress struct {
 	io.Reader
 	percentage float64
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
GitHub | 24 Jun 05:21 2016

[lxc/lxc] abf28d: Force lxc-instance to behave like a good Upstart c...

  Branch: refs/heads/master
  Home:   https://github.com/lxc/lxc
  Commit: abf28de1e6e9bddbca4b329dc277cddfc9c5b1e7
      https://github.com/lxc/lxc/commit/abf28de1e6e9bddbca4b329dc277cddfc9c5b1e7
  Author: AnrDaemon <anrdaemon@...>
  Date:   2016-06-24 (Fri, 24 Jun 2016)

  Changed paths:
    M config/init/upstart/lxc-instance.conf

  Log Message:
  -----------
  Force lxc-instance to behave like a good Upstart client

Remove unnecessary shell wrap around job start.
Force foreground execution to allow job monitoring and control.

Signed-off-by Andrey Repin <anrdaemon@...>

  Commit: b2a245c2b4538ebd1861882cdd2bb66176f031ca
      https://github.com/lxc/lxc/commit/b2a245c2b4538ebd1861882cdd2bb66176f031ca
  Author: Stéphane Graber <stgraber@...>
  Date:   2016-06-23 (Thu, 23 Jun 2016)

  Changed paths:
    M config/init/upstart/lxc-instance.conf

  Log Message:
  -----------
  Merge pull request #1056 from AnrDaemon/patch-1

Force lxc-instance to behave like a good Upstart client

Compare: https://github.com/lxc/lxc/compare/18f9cbeea741...b2a245c2b453
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
AnrDaemon on Github | 24 Jun 05:20 2016

[lxc/master] Force lxc-instance to behave like a good Upstart client

Attachment: text/x-mailbox, 629 bytes
From abf28de1e6e9bddbca4b329dc277cddfc9c5b1e7 Mon Sep 17 00:00:00 2001
From: AnrDaemon <anrdaemon <at> freemail.ru>
Date: Fri, 24 Jun 2016 06:19:51 +0300
Subject: [PATCH] Force lxc-instance to behave like a good Upstart client

Remove unnecessary shell wrap around job start.
Force foreground execution to allow job monitoring and control.

Signed-off-by Andrey Repin <anrdaemon <at> yandex.ru>
---
 config/init/upstart/lxc-instance.conf | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/config/init/upstart/lxc-instance.conf b/config/init/upstart/lxc-instance.conf
index 58d045d..1c9c3e6 100644
--- a/config/init/upstart/lxc-instance.conf
+++ b/config/init/upstart/lxc-instance.conf
 <at>  <at>  -17,6 +17,4  <at>  <at>  pre-start script
 	lxc-wait -s RUNNING -n $NAME -t 0 && { stop; exit 0; } || true
 end script

-script
-	exec lxc-start -n $NAME
-end script
+exec lxc-start -F -n $NAME
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
AnrDaemon on Github | 24 Jun 04:43 2016

[lxc-pkg-ubuntu/dpm-xenial] Force lxc-instance to behave like a good Upstart client

Attachment: text/x-mailbox, 689 bytes
From 69c04de1037d9eef00fed46eab29eef8955ba045 Mon Sep 17 00:00:00 2001
From: AnrDaemon <anrdaemon <at> freemail.ru>
Date: Fri, 24 Jun 2016 05:42:05 +0300
Subject: [PATCH] Force lxc-instance to behave like a good Upstart client

Upstart jobs are not supposed to daemonize unless abosolutely necessary. Which is normally not necessary
at all and just complicates the maintenance.

Signed-off-by Andrey Repin <anrdaemon <at> yandex.ru>
---
 config/init/upstart/lxc-instance.conf | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/config/init/upstart/lxc-instance.conf b/config/init/upstart/lxc-instance.conf
index 58d045d..b96fe16 100644
--- a/config/init/upstart/lxc-instance.conf
+++ b/config/init/upstart/lxc-instance.conf
 <at>  <at>  -17,6 +17,4  <at>  <at>  pre-start script
 	lxc-wait -s RUNNING -n $NAME -t 0 && { stop; exit 0; } || true
 end script

-script
-	exec lxc-start -n $NAME
-end script
+exec lxc-start -Fn $NAME
_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
tych0 on Github | 24 Jun 03:15 2016

[lxd/master] apparmor: create an apparmor namespace for each container

Attachment: text/x-mailbox, 1844 bytes
From 2f93569e660ded55d554a4a4e6a8065d94835b42 Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen <at> canonical.com>
Date: Fri, 17 Jun 2016 17:51:17 +0000
Subject: [PATCH] apparmor: create an apparmor namespace for each container

Note that this only allows privileged containers to load apparmor profiles, and
only then with something like:

diff --git a/config/apparmor/abstractions/container-base b/config/apparmor/abstractions/container-base
index fe24ff3..7138249 100644
--- a/config/apparmor/abstractions/container-base
+++ b/config/apparmor/abstractions/container-base
 <at>  <at>  -93,7 +93,7  <at>  <at> 
   mount fstype=sysfs -> /sys/,
   mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,
   deny /sys/firmware/efi/efivars/** rwklx,
-  deny /sys/kernel/security/** rwklx,
+  # deny /sys/kernel/security/** rwklx,
   mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
   mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,

We'll need to do something with the permissions on
/sys/kernel/security/apparmor to allow unprivileged users to write to it. I'll
be in touch with the security team about that, but for now I don't think this
hurts anything.

Signed-off-by: Tycho Andersen <tycho.andersen <at> canonical.com>
---
 lxd/apparmor.go      | 87 ++++++++++++++++++++++++++++++++++++++++++++--------
 lxd/container_lxc.go | 25 +++++++++++++--
 lxd/daemon.go        | 11 +++++++
 test/suites/basic.sh |  5 +--
 4 files changed, 111 insertions(+), 17 deletions(-)

diff --git a/lxd/apparmor.go b/lxd/apparmor.go
index ce25c50..c2c2327 100644
--- a/lxd/apparmor.go
+++ b/lxd/apparmor.go
 <at>  <at>  -47,6 +47,22  <at>  <at>  const NESTING_AA_PROFILE = `
   signal,
 `

+const DEFAULT_AA_NAMESPACE_PROFILE = `
+#include <tunables/global>
+profile "lxd-default" flags=(attach_disconnected,mediate_deleted) {
+    #include <abstractions/lxc/container-base>
+
+    # Special exception for cgroup namespaces
+    %s
+
+    # user input raw.apparmor below here
+    %s
+
+    # nesting support goes here if needed
+    %s
+    change_profile -> ":%s://*",
+}`
+
 const DEFAULT_AA_PROFILE = `
 #include <tunables/global>
 profile "%s" flags=(attach_disconnected,mediate_deleted) {
 <at>  <at>  -63,17 +79,35  <at>  <at>  profile "%s" flags=(attach_disconnected,mediate_deleted) {
     change_profile -> "%s",
 }`

-func AAProfileFull(c container) string {
-	lxddir := shared.VarPath("")
-	if len(c.Name())+len(lxddir)+7 >= 253 {
+func mkApparmorName(name string) string {
+	if len(name)+7 >= 253 {
 		hash := sha256.New()
-		io.WriteString(hash, lxddir)
-		lxddir = fmt.Sprintf("%x", hash.Sum(nil))
+		io.WriteString(hash, name)
+		return fmt.Sprintf("%x", hash.Sum(nil))
 	}

+	return name
+}
+
+func AANamespace(c container) string {
+	/* / is not allowed in apparmor namespace names; let's also trim the
+	 * leading / so it doesn't look like "-var-lib-lxd"
+	 */
+	lxddir := strings.Replace(shared.VarPath("")[1:], "/", "-", -1)
+	lxddir = mkApparmorName(lxddir)
 	return fmt.Sprintf("lxd-%s_<%s>", c.Name(), lxddir)
 }

+func AAProfileFull(c container) string {
+	if aaStacking {
+		return fmt.Sprintf(":%s://lxd-default", AANamespace(c))
+	} else {
+		lxddir := shared.VarPath("")
+		lxddir = mkApparmorName(lxddir)
+		return fmt.Sprintf("lxd-%s_<%s>", c.Name(), lxddir)
+	}
+}
+
 func AAProfileShort(c container) string {
 	return fmt.Sprintf("lxd-%s", c.Name())
 }
 <at>  <at>  -99,7 +133,26  <at>  <at>  func getAAProfileContent(c container) string {
 		nesting = NESTING_AA_PROFILE
 	}

-	return fmt.Sprintf(DEFAULT_AA_PROFILE, AAProfileFull(c), AAProfileCgns(), rawApparmor,
nesting, AAProfileFull(c))
+	if aaStacking {
+		return fmt.Sprintf(
+			DEFAULT_AA_NAMESPACE_PROFILE,
+			AAProfileCgns(),
+			rawApparmor,
+			nesting,
+			AANamespace(c),
+		)
+	} else {
+		full := AAProfileFull(c)
+
+		return fmt.Sprintf(
+			DEFAULT_AA_PROFILE,
+			full,
+			AAProfileCgns(),
+			rawApparmor,
+			nesting,
+			full,
+		)
+	}
 }

 func runApparmor(command string, c container) error {
 <at>  <at>  -107,12 +160,17  <at>  <at>  func runApparmor(command string, c container) error {
 		return nil
 	}

-	cmd := exec.Command("apparmor_parser", []string{
+	args := []string{
 		fmt.Sprintf("-%sWL", command),
 		path.Join(aaPath, "cache"),
 		path.Join(aaPath, "profiles", AAProfileShort(c)),
-	}...)
+	}

+	if aaStacking {
+		args = append([]string{"-n", AANamespace(c)}, args...)
+	}
+
+	cmd := exec.Command("apparmor_parser", args...)
 	output, err := cmd.CombinedOutput()
 	if err != nil {
 		shared.Log.Error("Running apparmor",
 <at>  <at>  -165,14 +223,19  <at>  <at>  func AALoadProfile(c container) error {
 	return runApparmor(APPARMOR_CMD_LOAD, c)
 }

-// Ensure that the container's policy is unloaded to free kernel memory. This
-// does not delete the policy from disk or cache.
-func AAUnloadProfile(c container) error {
+// Ensure that the container's policy namespace is unloaded to free kernel
+// memory. This does not delete the policy from disk or cache.
+func AADestroy(c container) error {
 	if !aaAdmin {
 		return nil
 	}

-	return runApparmor(APPARMOR_CMD_UNLOAD, c)
+	if aaStacking {
+		content := []byte(fmt.Sprintf(":%s:", AANamespace(c)))
+		return ioutil.WriteFile("/sys/kernel/security/apparmor/.remove", content, 0)
+	} else {
+		return runApparmor(APPARMOR_CMD_UNLOAD, c)
+	}
 }

 // Parse the profile without loading it into the kernel.
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 6fd0ee4..427e17c 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
 <at>  <at>  -293,7 +293,12  <at>  <at>  func (c *containerLXC) initLXC() error {
 	}

 	// Base config
-	err = lxcSetConfigItem(cc, "lxc.cap.drop", "mac_admin mac_override sys_time sys_module sys_rawio")
+	toDrop := "sys_time sys_module sys_rawio"
+	if !aaStacking || c.IsPrivileged() {
+		toDrop = toDrop + " mac_admin mac_override"
+	}
+
+	err = lxcSetConfigItem(cc, "lxc.cap.drop", toDrop)
 	if err != nil {
 		return err
 	}
 <at>  <at>  -490,7 +495,19  <at>  <at>  func (c *containerLXC) initLXC() error {
 			}
 		} else {
 			// If not currently confined, use the container's profile
-			err := lxcSetConfigItem(cc, "lxc.aa_profile", AAProfileFull(c))
+			profile := AAProfileFull(c)
+
+			/* In the unprivileged case, we're relying on the user
+			 * namespace to do all the access control. We can thus
+			 * just move the container into a namespace and leave
+			 * it unconfined, so it can load its own profiles if it
+			 * wants to.
+			 */
+			if aaStacking && !c.IsPrivileged() {
+				profile = fmt.Sprintf(":%s:", AANamespace(c))
+			}
+
+			err := lxcSetConfigItem(cc, "lxc.aa_profile", profile)
 			if err != nil {
 				return err
 			}
 <at>  <at>  -1450,7 +1467,9  <at>  <at>  func (c *containerLXC) OnStop(target string) error {
 	}

 	// Unload the apparmor profile
-	AAUnloadProfile(c)
+	if err := AADestroy(c); err != nil {
+		shared.Log.Error("failed to destroy apparmor namespace", log.Ctx{"container": c.Name(), "err": err})
+	}

 	// FIXME: The go routine can go away once we can rely on LXC_TARGET
 	go func(c *containerLXC, target string, wg *sync.WaitGroup) {
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 37a2338..8657746 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
 <at>  <at>  -41,6 +41,7  <at>  <at>  import (
 var aaAdmin = true
 var aaAvailable = true
 var aaConfined = false
+var aaStacking = false

 // CGroup
 var cgBlkioController = false
 <at>  <at>  -626,6 +627,16  <at>  <at>  func (d *Daemon) Init() error {
 		}
 	}

+	if aaAvailable {
+		content, err := ioutil.ReadFile("/sys/kernel/security/apparmor/features/domain/stack")
+		if err == nil && string(content) == "yes\n" {
+			aaStacking = true
+			shared.Log.Warn("Enabled apparmor stacking")
+		} else {
+			shared.Log.Warn("Kernel doesn't support apparmor stacking")
+		}
+	}
+
 	/* Detect CGroup support */
 	cgBlkioController = shared.PathExists("/sys/fs/cgroup/blkio/")
 	if !cgBlkioController {
diff --git a/test/suites/basic.sh b/test/suites/basic.sh
index 7e4f915..0f5f265 100644
--- a/test/suites/basic.sh
+++ b/test/suites/basic.sh
 <at>  <at>  -299,9 +299,10  <at>  <at>  test_basic_usage() {
   # check that an apparmor profile is created for this container, that it is
   # unloaded on stop, and that it is deleted when the container is deleted
   lxc launch testimage lxd-apparmor-test
-  aa-status | grep "lxd-lxd-apparmor-test_<${LXD_DIR}>"
+  aa_namespace="lxd-lxd-apparmor-test_<$(echo "${LXD_DIR}" | sed -e 's/\//-/g' -e 's/^.//')>"
+  aa-status | grep ":${aa_namespace}://lxd-default"
   lxc stop lxd-apparmor-test --force
-  ! aa-status | grep -q "lxd-lxd-apparmor-test_<${LXD_DIR}>"
+  ! aa-status | grep -q ":${aa_namespace}://lxd-default"
   lxc delete lxd-apparmor-test
   [ ! -f "${LXD_DIR}/security/apparmor/profiles/lxd-lxd-apparmor-test" ]

_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel
rockstar on Github | 24 Jun 01:05 2016

[pylxd/master] Integration cleanup

Attachment: text/x-mailbox, 831 bytes
From 8a37d59fa643eeb6f660c5162866c79ae7553fe5 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Thu, 23 Jun 2016 16:33:03 -0600
Subject: [PATCH 1/3] Pin ws4py to not use 0.3.5

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 66e56a2..62882d4 100644
--- a/requirements.txt
+++ b/requirements.txt
 <at>  <at>  -1,7 +1,7  <at>  <at> 
 pbr>=1.6
 python-dateutil>=2.4.2
 six>=1.9.0
-ws4py>=0.3.4
+ws4py!=0.3.5,>=0.3.4  # 0.3.5 is broken for websocket support
 requests!=2.8.0,>=2.5.2
 requests-unixsocket>=0.1.5
 cryptography>=1.4

From 4cb91bc7574c0d1bd5e72463a8ca803a23147bb1 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Thu, 23 Jun 2016 16:52:04 -0600
Subject: [PATCH 2/3] Remove deprecated codepaths from the integration tests

---
 integration/test_containers.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/integration/test_containers.py b/integration/test_containers.py
index cc9053e..1470b7e 100644
--- a/integration/test_containers.py
+++ b/integration/test_containers.py
 <at>  <at>  -116,29 +116,29  <at>  <at>  def test_snapshot(self):
         # NOTE: rockstar (15 Feb 2016) - Once again, multiple things
         # asserted in the same test.
         name = 'an-snapshot'
-        self.container.snapshot(name, wait=True)
+        snapshot = self.container.snapshots.create(name, wait=True)

-        self.assertEqual([name], self.container.list_snapshots())
+        self.assertEqual([name], [s.name for s in self.container.snapshots.all()])

         new_name = 'an-other-snapshot'
-        self.container.rename_snapshot(name, new_name, wait=True)
+        snapshot.rename(new_name, wait=True)

-        self.assertEqual([new_name], self.container.list_snapshots())
+        self.assertEqual([new_name], [s.name for s in self.container.snapshots.all()])

-        self.container.delete_snapshot(new_name, wait=True)
+        snapshot.delete(wait=True)

-        self.assertEqual([], self.container.list_snapshots())
+        self.assertEqual([], self.container.snapshots.all())

     def test_put_get_file(self):
         """A file is written to the container and then read."""
         filepath = '/tmp/an_file'
         data = b'abcdef'

-        retval = self.container.put_file(filepath, data)
+        retval = self.container.files.put(filepath, data)

         self.assertTrue(retval)

-        contents = self.container.get_file(filepath)
+        contents = self.container.files.get(filepath)

         self.assertEqual(data, contents)

From 5c96f29a1b1704dd14ca45481b5bcb0708994fff Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul.hummer <at> canonical.com>
Date: Thu, 23 Jun 2016 16:54:39 -0600
Subject: [PATCH 3/3] Fix lint

---
 integration/test_containers.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/integration/test_containers.py b/integration/test_containers.py
index 1470b7e..4e1f552 100644
--- a/integration/test_containers.py
+++ b/integration/test_containers.py
 <at>  <at>  -118,12 +118,14  <at>  <at>  def test_snapshot(self):
         name = 'an-snapshot'
         snapshot = self.container.snapshots.create(name, wait=True)

-        self.assertEqual([name], [s.name for s in self.container.snapshots.all()])
+        self.assertEqual(
+            [name], [s.name for s in self.container.snapshots.all()])

         new_name = 'an-other-snapshot'
         snapshot.rename(new_name, wait=True)

-        self.assertEqual([new_name], [s.name for s in self.container.snapshots.all()])
+        self.assertEqual(
+            [new_name], [s.name for s in self.container.snapshots.all()])

         snapshot.delete(wait=True)

_______________________________________________
lxc-devel mailing list
lxc-devel <at> lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Gmane