aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/resource/lxd
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/lxd')
-rw-r--r--vicn/resource/lxd/lxc_container.py89
-rw-r--r--vicn/resource/lxd/lxd_hypervisor.py45
-rw-r--r--vicn/resource/lxd/lxd_profile.py56
3 files changed, 131 insertions, 59 deletions
diff --git a/vicn/resource/lxd/lxc_container.py b/vicn/resource/lxd/lxc_container.py
index 9daaffb7..5670d1a2 100644
--- a/vicn/resource/lxd/lxc_container.py
+++ b/vicn/resource/lxd/lxc_container.py
@@ -18,16 +18,14 @@
import logging
import shlex
-import time
-# Suppress logging from pylxd dependency on ws4py
+# Suppress logging from pylxd dependency on ws4py
# (this needs to be included before pylxd)
from ws4py import configure_logger
configure_logger(level=logging.ERROR)
import pylxd
from netmodel.model.type import String, Integer, Bool, Self
-from vicn.core.address_mgr import AddressManager
from vicn.core.attribute import Attribute, Reference, Multiplicity
from vicn.core.commands import ReturnValue
from vicn.core.exception import ResourceNotFound
@@ -37,12 +35,10 @@ from vicn.core.task import task, inline_task, BashTask, EmptyTas
from vicn.resource.linux.net_device import NetDevice
from vicn.resource.node import Node
from vicn.resource.vpp.scripts import APPARMOR_VPP_PROFILE
+from vicn.resource.lxd.lxd_profile import LXD_PROFILE_DEFAULT_IFNAME
log = logging.getLogger(__name__)
-# Default name of VICN management/monitoring interface
-DEFAULT_LXC_NETDEVICE = 'eth0'
-
# Default remote server (pull mode only)
DEFAULT_SOURCE_URL = 'https://cloud-images.ubuntu.com/releases/'
@@ -56,8 +52,10 @@ CMD_UNSET_IP6_FWD = 'sysctl -w net.ipv6.conf.all.forwarding=0'
CMD_SET_IP6_FWD = 'sysctl -w net.ipv6.conf.all.forwarding=1'
CMD_GET_IP6_FWD = 'sysctl -n net.ipv6.conf.all.forwarding'
+CMD_NETWORK_DHCP='dhclient {container.management_interface.device_name}'
+
# Type: ContainerName
-ContainerName = String(max_size = 64, ascii = True,
+ContainerName = String(max_size = 64, ascii = True,
forbidden = ('/', ',', ':'))
class LxcContainer(Node):
@@ -74,12 +72,12 @@ class LxcContainer(Node):
architecture = Attribute(String, description = 'Architecture',
default = 'x86_64')
- container_name = Attribute(ContainerName,
+ container_name = Attribute(ContainerName,
description = 'Name of the container',
default = Reference(Self, 'name'))
ephemeral = Attribute(Bool, description = 'Ephemeral container flag',
default = False)
- node = Attribute(Node,
+ node = Attribute(Node,
description = 'Node on which the container is running',
mandatory = True,
requirements = [
@@ -91,26 +89,26 @@ class LxcContainer(Node):
Requirement('bridge'),
# A DNS server is required to provide internet connectivity to
# the containers
- Requirement('dns_server'),
+ # Requirement('dns_server'),
])
- profiles = Attribute(String, multiplicity = Multiplicity.OneToMany,
- default = ['default'])
+ profiles = Attribute(String, multiplicity = Multiplicity.OneToMany,
+ default = ['vicn'])
image = Attribute(String, description = 'image', default = None)
is_image = Attribute(Bool, defaut = False)
pid = Attribute(Integer, description = 'PID of the container')
ip6_forwarding = Attribute(Bool, default=True)
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Constructor / Accessors
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._container = None
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Resource lifecycle
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
@inline_task
def __initialize__(self):
@@ -120,11 +118,11 @@ class LxcContainer(Node):
self.node_with_kernel = Reference(self, 'node')
# We automatically add the management/monitoring interface
- self._host_interface = NetDevice(node = self,
+ self._management_interface = NetDevice(node = self,
owner = self,
monitored = False,
- device_name = DEFAULT_LXC_NETDEVICE)
- self._state.manager.commit_resource(self._host_interface)
+ device_name = LXD_PROFILE_DEFAULT_IFNAME)
+ self._state.manager.commit_resource(self._management_interface)
for iface in self.interfaces:
if iface.get_type() == "dpdkdevice":
@@ -150,7 +148,9 @@ class LxcContainer(Node):
wait_vpp_host = wait_resource_task(self.node.vpp_host)
create = self._create_container()
start = self.__method_start__()
- return wait_vpp_host > (create > start)
+ #XXX Should be an option on the netdevice
+ dhcp_interface = BashTask(self, CMD_NETWORK_DHCP, {'container':self})
+ return (wait_vpp_host > (create > start)) > dhcp_interface
@task
def _create_container(self):
@@ -158,54 +158,52 @@ class LxcContainer(Node):
log.debug('Container description: {}'.format(container))
client = self.node.lxd_hypervisor.client
self._container = client.containers.create(container, wait=True)
- self._container.start(wait = True)
+ #self._container.start(wait = True)
def _get_container_description(self):
# Base configuration
container = {
- 'name' : self.container_name,
+ 'name' : self.container_name,
'architecture' : self.architecture,
- 'ephemeral' : self.ephemeral,
- 'profiles' : ['default'],
+ 'ephemeral' : self.ephemeral,
+ 'profiles' : self.profiles,
'config' : {},
'devices' : {},
}
# DEVICES
- devices = {}
# FIXME Container profile support is provided by setting changes into
# configuration (currently only vpp profile is supported)
for profile in self.profiles:
if profile == 'vpp':
# Set the new apparmor profile. This will be created in VPP
- # application
+ # application
# Mount hugetlbfs in the container.
container['config']['raw.lxc'] = APPARMOR_VPP_PROFILE
container['config']['security.privileged'] = 'true'
for device in self.node.vpp_host.uio_devices:
container['devices'][device] = {
- 'path' : '/dev/{}'.format(device),
+ 'path' : '/dev/{}'.format(device),
'type' : 'unix-char' }
- # NETWORK (not for images)
-
- if not self.is_image:
- container['config']['user.network_mode'] = 'link-local'
- device = {
- 'type' : 'nic',
- 'name' : self.host_interface.device_name,
- 'nictype' : 'bridged',
- 'parent' : self.node.bridge.device_name,
- }
- device['hwaddr'] = AddressManager().get_mac(self)
- prefix = 'veth-{}'.format(self.container_name)
- device['host_name'] = AddressManager().get('device_name', self,
- prefix = prefix, scope = prefix)
-
- container['devices'][device['name']] = device
-
+# # NETWORK (not for images)
+#
+# if not self.is_image:
+# container['config']['user.network_mode'] = 'link-local'
+# device = {
+# 'type' : 'nic',
+# 'name' : self.host_interface.device_name,
+# 'nictype' : 'bridged',
+# 'parent' : self.node.bridge.device_name,
+# }
+# device['hwaddr'] = AddressManager().get_mac(self)
+# prefix = 'veth-{}'.format(self.container_name)
+# device['host_name'] = AddressManager().get('device_name', self,
+# prefix = prefix, scope = prefix)
+#
+# container['devices'][device['name']] = device
# SOURCE
@@ -233,6 +231,7 @@ class LxcContainer(Node):
@task
def __delete__(self):
log.info("Delete container {}".format(self.container_name))
+ import pdb; pdb.set_trace()
self.node.lxd_hypervisor.client.containers.remove(self.name)
#--------------------------------------------------------------------------
@@ -243,7 +242,7 @@ class LxcContainer(Node):
"""
Attribute: pid (getter)
"""
- return BashTask(self.node, CMD_GET_PID, {'container': self},
+ return BashTask(self.node, CMD_GET_PID, {'container': self},
parse = lambda rv: {'pid': rv.stdout.strip()})
#--------------------------------------------------------------------------
diff --git a/vicn/resource/lxd/lxd_hypervisor.py b/vicn/resource/lxd/lxd_hypervisor.py
index 68b7ab28..f9952e4f 100644
--- a/vicn/resource/lxd/lxd_hypervisor.py
+++ b/vicn/resource/lxd/lxd_hypervisor.py
@@ -38,6 +38,7 @@ from vicn.core.task import BashTask, task
from vicn.resource.linux.application import LinuxApplication as Application
from vicn.resource.linux.service import Service
from vicn.resource.linux.certificate import Certificate
+from vicn.resource.lxd.lxd_profile import LxdProfile
# Suppress non-important logging messages from requests and urllib3
logging.getLogger("requests").setLevel(logging.WARNING)
@@ -52,19 +53,20 @@ DEFAULT_KEY_PATH = os.path.expanduser(os.path.join(
'~', '.vicn', 'lxd_client_cert', 'client_key.pem'))
# FIXME hardcoded password for LXD server
-DEFAULT_TRUST_PASSWORD = 'vicn'
+LXD_TRUST_PWD_DEFAULT = 'vicn'
-DEFAULT_LXD_STORAGE = 100 # GB
+LXD_STORAGE_SIZE_DEFAULT = 100 # GB
+LXD_NETWORK_DEFAULT = 'lxdbr-vicn'
+LXD_PROFILE_NAME_DEFAULT = 'vicn'
+ZFS_DEFAULT_POOL_NAME = 'vicn'
# Commands used to interact with the LXD hypervisor
CMD_LXD_CHECK_INIT = 'lsof -i:{lxd.lxd_port}'
CMD_LXD_INIT_BASE = 'lxd init --auto '
-CMD_LXD_INIT='''
-{base}
-lxc profile unset default environment.http_proxy
-lxc profile unset default user.network_mode
-'''
+
+CMD_LXD_NETWORK_GET = 'lxc network list | grep {lxd_hypervisor.network}'
+CMD_LXD_NETWORK_SET = 'lxc network create {lxd_hypervisor.network} || true'
#------------------------------------------------------------------------------
# Subresources
@@ -82,7 +84,7 @@ class LxdInit(Application):
'storage-backend' : self.owner.storage_backend,
'network-port' : self.owner.lxd_port,
'network-address' : '0.0.0.0',
- 'trust-password' : DEFAULT_TRUST_PASSWORD,
+ 'trust-password' : self.owner.trust_password,
}
if self.owner.storage_backend == 'zfs':
@@ -104,8 +106,7 @@ class LxdInit(Application):
# error: Failed to create the ZFS pool: The ZFS modules are not loaded.
# Try running '/sbin/modprobe zfs' as root to load them.
# zfs-dkms in the host
- return BashTask(self.owner.node, CMD_LXD_INIT, {'base': cmd},
- as_root = True)
+ return BashTask(self.owner.node, cmd, as_root = True)
def __delete__(self):
raise NotImplementedError
@@ -134,7 +135,7 @@ class LxdInstallCert(Resource):
client certificate for the LXD daemon.
"""
log.info('Adding certificate on LXD')
- self.owner.client.authenticate(DEFAULT_TRUST_PASSWORD)
+ self.owner.client.authenticate(self.owner.trust_password)
if not self.owner.client.trusted:
raise Exception
@@ -154,9 +155,13 @@ class LxdHypervisor(Service):
default = 'zfs',
choices = ['zfs'])
storage_size = Attribute(Integer, description = 'Storage size',
- default = DEFAULT_LXD_STORAGE) # GB
+ default = LXD_STORAGE_SIZE_DEFAULT) # GB
zfs_pool = Attribute(String, description = 'ZFS pool',
- default='vicn')
+ default=ZFS_DEFAULT_POOL_NAME)
+ network = Attribute(String, description = 'LXD network name',
+ default=LXD_NETWORK_DEFAULT)
+ trust_password = Attribute(String, description = 'Trust password for the LXD server',
+ default=LXD_TRUST_PWD_DEFAULT)
# Just overload attribute with a new reverse
node = Attribute(
@@ -194,8 +199,13 @@ class LxdHypervisor(Service):
owner = self)
lxd_cert_install = LxdInstallCert(certificate = lxd_local_cert,
owner = self)
+ lxd_vicn_profile = LxdProfile(name=LXD_PROFILE_NAME_DEFAULT,
+ node=self.node,
+ description='vICN profile',
+ network=self.network,
+ pool=self.zfs_pool)
- return (lxd_init | lxd_local_cert) > lxd_cert_install
+ return (lxd_init | lxd_local_cert) > (lxd_vicn_profile | lxd_cert_install)
#--------------------------------------------------------------------------
# Private methods
@@ -221,3 +231,10 @@ class LxdHypervisor(Service):
@property
def aliases(self):
return [alias for image in self.images for alias in image.aliases]
+
+ @task
+ def _get_network(self):
+ return None #XXX We assume it's always nothing
+
+ def _set_network(self):
+ return BashTask(self.node, CMD_LXD_NETWORK_SET, {'lxd_hypervisor': self})
diff --git a/vicn/resource/lxd/lxd_profile.py b/vicn/resource/lxd/lxd_profile.py
new file mode 100644
index 00000000..e7afee4a
--- /dev/null
+++ b/vicn/resource/lxd/lxd_profile.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# 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 vicn.core.resource import Resource
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.task import BashTask
+from vicn.core.exception import ResourceNotFound
+
+CMD_LXD_PROFILE_CREATE = '''
+lxc profile create {profile.name} description="{profile.description}"
+lxc profile device add {profile.name} root disk pool={profile.pool} path=/
+lxc profile device add {profile.name} {profile.iface_name} nic name={profile.iface_name} nictype=bridged parent={profile.network}
+lxc profile unset {profile.name} environment.http_proxy
+lxc profile unset {profile.name} user.network_mode
+'''
+
+CMD_LXD_PROFILE_GET = 'lxc profile list | grep {profile.name}'
+
+# Default name of VICN management/monitoring interface
+#
+# This should be kept in sync with /etc/network/interfaces in the image file so that dhcp works
+LXD_PROFILE_DEFAULT_IFNAME = 'vicn_mgmt'
+
+class LxdProfile(Resource):
+
+ description = Attribute(String, descr="profile description", mandatory=True)
+ pool = Attribute(String, descr="ZFS pool used by the containers", mandatory=True)
+ network = Attribute(String, description='Network on which to attach', mandatory=True)
+ iface_name = Attribute(String, description='Default interface name',
+ default = LXD_PROFILE_DEFAULT_IFNAME)
+ node = Attribute(Resource, mandatory=True)
+
+ def __get__(self):
+ def parse(rv):
+ if not rv.stdout:
+ raise ResourceNotFound
+ return BashTask(self.node, CMD_LXD_PROFILE_GET, {'profile':self}, parse=parse)
+
+ def __create__(self):
+ return BashTask(self.node, CMD_LXD_PROFILE_CREATE, {'profile':self})