diff options
author | Marcel Enguehard <mengueha+fdio@cisco.com> | 2017-07-19 11:26:26 +0200 |
---|---|---|
committer | Marcel Enguehard <mengueha+fdio@cisco.com> | 2017-07-19 11:51:26 +0000 |
commit | 3e6678f9c692553e8902da4d6fb1fe6c087db1f4 (patch) | |
tree | 580a46ca5de22a044319eabb295ad980d50589ec /vicn/resource/linux | |
parent | 08c4f765cf29dbd6e9a616c542552417eece14fc (diff) |
* GUI resource
* MemIf interface for VPP
* Better netmodel integration
* Draft documentation
* New tutorials
* Improved monitoring and error handling
* Refactored IP addresses and prefixes representation
* Improved image mgmt for LXD
* Various bugfixes and code refactoring
Change-Id: I90da6cf7b5716bc7deb6bf4e24d3f9f01b5a9b0f
Signed-off-by: Marcel Enguehard <mengueha+fdio@cisco.com>
Diffstat (limited to 'vicn/resource/linux')
26 files changed, 597 insertions, 239 deletions
diff --git a/vicn/resource/linux/bridge.py b/vicn/resource/linux/bridge.py index 7b5ceed7..edc8acdc 100644 --- a/vicn/resource/linux/bridge.py +++ b/vicn/resource/linux/bridge.py @@ -22,18 +22,15 @@ from vicn.core.address_mgr import AddressManager from vicn.core.attribute import Attribute, Multiplicity from vicn.core.exception import ResourceNotFound from vicn.core.requirement import Requirement -from vicn.core.task import inline_task +from vicn.core.task import inline_task, inherit from vicn.resource.channel import Channel +from vicn.resource.interface import Interface from vicn.resource.linux.bridge_mgr import BridgeManager -from vicn.resource.linux.net_device import BaseNetDevice +from vicn.resource.linux.net_device import NetDevice log = logging.getLogger(__name__) -# FIXME This should use the AddressManager to get allocated a name that does -# not exist -DEFAULT_BRIDGE_NAME = 'br0' - -class Bridge(Channel, BaseNetDevice): +class Bridge(Channel, NetDevice): """ Resource: Bridge """ @@ -62,21 +59,10 @@ class Bridge(Channel, BaseNetDevice): # Resource lifecycle #-------------------------------------------------------------------------- - @inline_task - def __get__(self): - # FIXME we currently force the recreation of the bridge, delegating the - # check to the creation function - raise ResourceNotFound - + @inherit(Channel, Interface) def __create__(self): - # FIXME : reserves .1 IP address for the bridge, provided no other - # class uses this trick - AddressManager().get_ip(self) return self.node.bridge_manager.add_bridge(self.device_name) - # Everything should be handled by BaseNetDevice - __delete__ = None - #-------------------------------------------------------------------------- # Resource lifecycle #-------------------------------------------------------------------------- @@ -86,7 +72,7 @@ class Bridge(Channel, BaseNetDevice): Returns: Task """ - return self.node.bridge_manager.add_interface(self.device_name, + return self.node.bridge_manager.add_interface(self.device_name, interface.device_name, vlan) def __method_add_interface__(self, interface, vlan=None): @@ -99,6 +85,6 @@ class Bridge(Channel, BaseNetDevice): """ log.info('Removing interface {} from bridge {}'.format( interface.device_name, self.name)) - return self.node.bridge_manager.del_interface(self.device_name, + return self.node.bridge_manager.del_interface(self.device_name, interface.device_name) diff --git a/vicn/resource/linux/certificate.py b/vicn/resource/linux/certificate.py index dd451770..2cc6c9b0 100644 --- a/vicn/resource/linux/certificate.py +++ b/vicn/resource/linux/certificate.py @@ -23,6 +23,7 @@ from vicn.core.attribute import Attribute, Multiplicity, Reference from vicn.core.exception import ResourceNotFound from vicn.core.resource import Resource from vicn.core.task import task, inline_task, BashTask +from vicn.core.task import inherit_parent from vicn.resource.linux.file import File from vicn.resource.node import Node @@ -49,29 +50,40 @@ class Certificate(Resource): multiplicity = Multiplicity.ManyToOne) cert = Attribute(String, description = 'Certificate path', mandatory = True) - key = Attribute(String, description = 'Key path', - mandatory = True) + key = Attribute(String, description = 'Key path') #-------------------------------------------------------------------------- # Resource lifecycle #-------------------------------------------------------------------------- - @inline_task - def __initialize__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self._cert_file = File(node = Reference(self, 'node'), filename = Reference(self, 'cert'), managed = False) - self._key_file = File(node = Reference(self, 'node'), - filename = Reference(self, 'key'), - managed = False) + if self.key: + self._key_file = File(node = Reference(self, 'node'), + filename = Reference(self, 'key'), + managed = False) + else: + self._key_file = None + @inherit_parent def __get__(self): - return self._cert_file.__get__() | self._key_file.__get__() + if self.key: + return self._cert_file.__get__() | self._key_file.__get__() + else: + return self._cert_file.__get__() + @inherit_parent def __create__(self): return BashTask(self.node, CMD_CREATE, {'self': self}) + @inherit_parent def __delete__(self): - return self._cert_file.__delete__() | self._key_file.__delete__() + if self.key: + return self._cert_file.__delete__() | self._key_file.__delete__() + else: + return self._cert_file.__delete__() diff --git a/vicn/resource/linux/certificate_store.py b/vicn/resource/linux/certificate_store.py new file mode 100644 index 00000000..59d9a239 --- /dev/null +++ b/vicn/resource/linux/certificate_store.py @@ -0,0 +1,54 @@ +#!/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.attribute import Attribute, Multiplicity +from vicn.core.resource import Resource +from vicn.resource.linux.certificate import Certificate +from vicn.resource.node import Node + +PACKAGE = 'ca-certificates' + +CMD_ADD_CERTIFICATE = ''' +cp {certificate.cert} {store.PATH} +dpkg-reconfigure ca-certificates +''' + +class CertificateStore(Resource): + """ + Resource: System-wide Certificate Store + + This resource allows manipulation of the trusted certificates on the system. + Use with care. + + TODO: + - Ensure ca-certificates package is installed. + - Issue a warning to the user when it is used. + """ + + PATH = '/usr/share/ca-certificates' + + certificates = Attribute(Certificate, multiplicity = Multiplicity.OneToMany) + node = Attribute(Node, mandatory = True, requirements = [ + #Requirement(PACKAGE, 'in', 'packages') + ]) + + def _add_certificate(self): + # Return a task that takes a certificate as parameter + PARAM = None + return BashTask(self.node, CMD_ADD_CERTIFICATE, {'store': self, 'certificate': PARAM}, + root = True) diff --git a/vicn/resource/linux/dnsmasq.py b/vicn/resource/linux/dnsmasq.py index b5aa8053..70358ecb 100644 --- a/vicn/resource/linux/dnsmasq.py +++ b/vicn/resource/linux/dnsmasq.py @@ -24,6 +24,7 @@ from string import Template from netmodel.model.type import String, Bool from vicn.core.attribute import Attribute from vicn.core.resource import EmptyResource +from vicn.core.task import inherit_parent, override_parent from vicn.resource.dns_server import DnsServer from vicn.resource.interface import Interface from vicn.resource.linux.file import TextFile @@ -72,15 +73,16 @@ class DnsMasq(Service, DnsServer): log_dhcp = Attribute(Bool, description = 'Flag: log DHCP queries', default = True) - #-------------------------------------------------------------------------- - # Resource lifecycle - #-------------------------------------------------------------------------- - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not self.interface: raise Exception("Cannot initialize bridge without interface") + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + + @inherit_parent def __subresources__(self): # Overwrite configuration file flags = list() @@ -112,3 +114,7 @@ class DnsMasq(Service, DnsServer): return TextFile(node = self.node, owner = self, filename = FN_CONF, content = conf, overwrite = True) + + @override_parent + def __create__(self): + return self.__method_stop_start() diff --git a/vicn/resource/linux/file.py b/vicn/resource/linux/file.py index 44b4b5be..2a62f60e 100644 --- a/vicn/resource/linux/file.py +++ b/vicn/resource/linux/file.py @@ -16,11 +16,12 @@ # limitations under the License. # +from netmodel.model.key import Key from netmodel.model.type import String, Bool from vicn.core.attribute import Attribute, Multiplicity from vicn.core.exception import ResourceNotFound from vicn.core.resource import Resource -from vicn.core.task import BashTask, inline_task +from vicn.core.task import BashTask, inline_task, inherit_parent from vicn.resource.node import Node CREATE_DIR_CMD = "mkdir -p {dir}" @@ -38,22 +39,23 @@ class File(Resource): Resource: File """ filename = Attribute(String, description = 'Path to the file', - key = True, mandatory = True) node = Attribute(Node, description = 'Node on which the file is created', mandatory = True, multiplicity = Multiplicity.ManyToOne, reverse_name = 'files', - key = True, reverse_description = 'Files created on the node') overwrite = Attribute(Bool, description = 'Determines whether an existing file is overwritten', default = False) + __key__ = Key(node, filename) + #-------------------------------------------------------------------------- # Resource lifecycle #-------------------------------------------------------------------------- + @inherit_parent def __get__(self): # UGLY @inline_task @@ -72,12 +74,14 @@ class File(Resource): test = BashTask(self.node, GET_FILE_CMD, {"file": self}, parse=is_path) return test + @inherit_parent def __create__(self): ctask = BashTask(self.node, CREATE_FILE_CMD, {"file": self}) if self.overwrite: ctask = BashTask(self.node, DELETE_FILE_CMD, {'file': self}) > ctask return ctask + @inherit_parent def __delete__(self): return BashTask(self.node, DELETE_FILE_CMD, { "file" : self}) @@ -96,6 +100,8 @@ class TextFile(File): # Resource lifecycle #-------------------------------------------------------------------------- + # XXX REDUNDANT !!! + @inherit_parent def __create__(self): return BashTask(self.node, CMD_PRINT_TO_FILE, {'file': self}) diff --git a/vicn/resource/linux/folder.py b/vicn/resource/linux/folder.py new file mode 100644 index 00000000..636ecbcd --- /dev/null +++ b/vicn/resource/linux/folder.py @@ -0,0 +1,88 @@ +#!/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 netmodel.model.key import Key +from netmodel.model.type import String, Bool, Integer +from vicn.core.attribute import Attribute, Multiplicity +from vicn.core.exception import ResourceNotFound +from vicn.core.resource import Resource +from vicn.core.task import BashTask, inline_task, EmptyTask +from vicn.resource.node import Node + +CREATE_FOLDER_CMD = "mkdir -p {folder.foldername}" +DELETE_FOLDER_CMD = "rm -f {folder.foldername}" + +GET_FOLDER_CMD = 'test -d {folder.foldername} && readlink -e {folder.foldername}' + +SET_FOLDER_PERMISSION_CMD = 'chmod {folder.permission} {folder.foldername}' + +class Folder(Resource): + """ + Resource: Folder + """ + foldername = Attribute(String, description = 'Path to the folder', + mandatory = True) + node = Attribute(Node, description = 'Node on which the directory is created', + mandatory = True, + multiplicity = Multiplicity.ManyToOne, + reverse_name = 'folders', + reverse_description = 'Folders created on the node') + overwrite = Attribute(Bool, + description = 'Determines whether an existing folder is overwritten', + default = False) + permission = Attribute(Integer, + description = 'Permission to set in the folder', + default = 775) + + __key__ = Key(node, foldername) + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + + @inline_task + def __get__(self): + # UGLY +# @inline_task +# def not_found(): + raise ResourceNotFound + + # if self.overwrite: + # return not_found() + + # def is_path (rv): + # if rv is None or rv.stdout is None or len(rv.stdout) == 0 or \ + # rv.return_value != 0: + # raise ResourceNotFound + # return {} # 'filename': rv.stdout} + + # create = BashTask(self.node, GET_FOLDER_CMD, {"folder": self}, parse=is_path) + + # return create + + def __create__(self): + ctask = BashTask(self.node, CREATE_FOLDER_CMD, {"folder": self}) + + if self.overwrite: + ctask = BashTask(self.node, DELETE_FOLDER_CMD, {'folder': self}) > ctask + + set_permission = BashTask(self.node, SET_FOLDER_PERMISSION_CMD, {"folder": self}) + + return ctask > set_permission + + def __delete__(self): + return BashTask(self.node, DELETE_FOLFER_CMD, { "folder" : self}) diff --git a/vicn/resource/linux/gre_tunnel.py b/vicn/resource/linux/gre_tunnel.py new file mode 100644 index 00000000..c5a25307 --- /dev/null +++ b/vicn/resource/linux/gre_tunnel.py @@ -0,0 +1,63 @@ +#!/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. + +import string + +from netmodel.model.type import Integer, String +from vicn.core.attribute import Attribute +from vicn.core.resource import Resource +from vicn.core.task import BashTask, inherit_parent, override_parent +from vicn.resource.channel import Channel +from vicn.resource.linux.net_device import NetDevice, SlaveNetDevice + +CMD_CREATE_GRE_TUNNEL=''' +ip tunnel add {device_name} mode gre remote {dst} local {src} ttl 255 +''' + +class GREChannel(Channel): + """ + Resource: GRETunnel + """ + pass + +class GREInterface(SlaveNetDevice): + + remote_address = Attribute(String, description ='', + mandatory = True) + + @override_parent + def __create__(self): + return BashTask(self.src_interface.node, CMD_CREATE_GRE_TUNNEL, { + 'device_name': self.device_name, + 'src': str(self.parent.ip4_address), + 'dst': self.remote_address}) + +class GRETunnel(Resource): + + src_interface = Attribute(NetDevice, description = 'source interface', + mandatory = True) + dst_interface = Attribute(NetDevice, description = 'destination interface', + mandatory = True) + + @inherit_parent + def __subresources__(self): + channel = GREChannel() + src = GREInterface(node=src_interface.node, device_name="gre0", + parent=src_interface, channel=channel) + dst = GREInterface(node=dst_interface.node, device_name="gre0", + parent=dst_interface, channel=channel) + return (src | dst) | channel diff --git a/vicn/resource/linux/iperf.py b/vicn/resource/linux/iperf.py index a0780a1c..e4b8e94c 100644 --- a/vicn/resource/linux/iperf.py +++ b/vicn/resource/linux/iperf.py @@ -16,12 +16,10 @@ # limitations under the License. # -from abc import ABC - from netmodel.model.type import Integer from vicn.core.attribute import Attribute from vicn.resource.linux.application import LinuxApplication -class Iperf3(LinuxApplication, ABC): +class Iperf3(LinuxApplication): __package_names__ = ['iperf3'] diff --git a/vicn/resource/linux/keypair.py b/vicn/resource/linux/keypair.py index 66c98e5b..b748b756 100644 --- a/vicn/resource/linux/keypair.py +++ b/vicn/resource/linux/keypair.py @@ -23,6 +23,7 @@ from vicn.core.attribute import Attribute, Multiplicity, Reference from vicn.core.exception import ResourceNotFound from vicn.core.resource import Resource from vicn.core.task import task, inline_task, BashTask +from vicn.core.task import inherit_parent from vicn.resource.linux.file import File from vicn.resource.node import Node @@ -48,8 +49,8 @@ class Keypair(Resource): # Resource lifecycle #-------------------------------------------------------------------------- - @inline_task - def __initialize__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self._pubkey_file = File(node = Reference(self, 'node'), filename = self.key + '.pub', managed = False) @@ -57,14 +58,17 @@ class Keypair(Resource): filename = self.key, managed = False) + @inherit_parent def __get__(self): return self._pubkey_file.__get__() | self._key_file.__get__() + @inherit_parent def __create__(self): return BashTask(self.node, CMD_CREATE, { 'dirname': os.path.dirname(self.key), 'self': self}) + @inherit_parent def __delete__(self): return self._pubkey_file.__delete__() | self._key_file.__delete__() diff --git a/vicn/resource/linux/link.py b/vicn/resource/linux/link.py index da41fbe1..3ffaae97 100644 --- a/vicn/resource/linux/link.py +++ b/vicn/resource/linux/link.py @@ -20,12 +20,14 @@ import random import string import logging +from netmodel.model.key import Key from netmodel.model.type import Integer, String from vicn.core.attribute import Attribute, Reference from vicn.core.exception import ResourceNotFound from vicn.core.state import ResourceState, AttributeState from vicn.core.task import inline_task, async_task, run_task from vicn.core.task import get_attributes_task, BashTask +from vicn.core.task import inherit_parent from vicn.resource.channel import Channel from vicn.resource.interface import Interface from vicn.resource.linux.net_device import NonTapBaseNetDevice @@ -53,7 +55,7 @@ ip link set dev {tmp_src} netns {pid} name {interface.device_name} ip link set dev {tmp_dst} up ovs-vsctl add-port {host.bridge.device_name} {tmp_dst}''' -CMD_UP=''' +CMD_SET_UP=''' ip link set dev {interface.device_name} up ''' @@ -73,18 +75,19 @@ class Link(Channel): delay = Attribute(String, description = 'Link propagation delay') src_node = Attribute(Node, description = 'Source node', - key = True, mandatory = True) dst_node = Attribute(Node, description = 'Destination node', - key = True, mandatory = True) + __key__ = Key(src_node, dst_node) + def __init__(self, *args, **kwargs): assert 'src_node' in kwargs and 'dst_node' in kwargs self._src = None self._dst = None super().__init__(*args, **kwargs) + @inherit_parent @inline_task def __initialize__(self): # We create two managed net devices that are pre-setup @@ -124,23 +127,27 @@ class Link(Channel): vpp_src = VPPInterface(parent = self._src, vpp = self.src_node.vpp, ip4_address = Reference(self._src, 'ip4_address'), - device_name = 'vpp' + self._src.device_name) + ip6_address = Reference(self._src, 'ip6_address'), + device_name = 'host-' + self._src.device_name) manager.commit_resource(vpp_src) if hasattr(self.dst_node, 'vpp') and not self.dst_node.vpp is None: vpp_dst = VPPInterface(parent = self._dst, vpp = self.dst_node.vpp, ip4_address = Reference(self._dst, 'ip4_address'), - device_name = 'vpp' + self._dst.device_name) + ip6_address = Reference(self._dst, 'ip6_address'), + device_name = 'host-' + self._dst.device_name) manager.commit_resource(vpp_dst) #-------------------------------------------------------------------------- # Resource lifecycle #-------------------------------------------------------------------------- + @inherit_parent def __get__(self): return (self._src.__get__() | self._dst.__get__()) > async_task(self._commit)() + @inherit_parent def __create__(self): assert self.src_node.get_type() == 'lxccontainer' assert self.dst_node.get_type() == 'lxccontainer' @@ -168,8 +175,8 @@ class Link(Channel): host = src_host create = BashTask(host, CMD_CREATE, {'interface': self, 'tmp_src': tmp_src, 'tmp_dst': tmp_dst}) - up_src = BashTask(self.src_node, CMD_UP, {'interface': self._src}) - up_dst = BashTask(self.dst_node, CMD_UP, {'interface': self._dst}) + up_src = BashTask(self.src_node, CMD_SET_UP, {'interface': self._src}) + up_dst = BashTask(self.dst_node, CMD_SET_UP, {'interface': self._dst}) up = up_src | up_dst delif = delif_src | delif_dst return ((delif > (pid @ create)) > up) > async_task(self._commit)() @@ -178,10 +185,11 @@ class Link(Channel): 'tmp_src': tmp_src, 'tmp_dst': tmp_dst, 'host' : src_host}) create2 = BashTask(dst_host, CMD_CREATE_BR_TO_LXC, {'interface': self._dst, 'tmp_src': tmp_dst, 'tmp_dst': tmp_src, 'host' : dst_host}) - up_src = BashTask(self.src_node, CMD_UP, {'interface': self._src}) - up_dst = BashTask(self.dst_node, CMD_UP, {'interface': self._dst}) + up_src = BashTask(self.src_node, CMD_SET_UP, {'interface': self._src}) + up_dst = BashTask(self.dst_node, CMD_SET_UP, {'interface': self._dst}) return (((pid_src @ create) | (pid_dst @ create2)) > (up_src | up_dst)) > async_task(self._commit)() + @inherit_parent def __delete__(self): return self._src.__delete__() | self._dst.__delete__() diff --git a/vicn/resource/linux/macvlan.py b/vicn/resource/linux/macvlan.py index ea9c37c1..3c81cde1 100644 --- a/vicn/resource/linux/macvlan.py +++ b/vicn/resource/linux/macvlan.py @@ -18,14 +18,15 @@ from netmodel.model.type import String from vicn.core.attribute import Attribute -from vicn.core.task import BashTask -from vicn.resource.linux.net_device import SlaveBaseNetDevice +from vicn.core.task import BashTask, inherit +from vicn.resource.interface import Interface +from vicn.resource.linux.net_device import SlaveNetDevice CMD_CREATE_PARENT = 'ip link add name {netdevice.device_name} ' \ 'link {netdevice.parent.device_name} ' \ 'type {netdevice.netdevice_type} mode {netdevice.mode}' -class MacVlan(SlaveBaseNetDevice): +class MacVlan(SlaveNetDevice): """ Resource: MacVlan @@ -48,5 +49,6 @@ class MacVlan(SlaveBaseNetDevice): # Resource lifecycle #-------------------------------------------------------------------------- + @inherit(Interface) def __create__(self): return BashTask(self.node, CMD_CREATE_PARENT, {'netdevice': self}) diff --git a/vicn/resource/linux/macvtap.py b/vicn/resource/linux/macvtap.py index 82002e02..23176763 100644 --- a/vicn/resource/linux/macvtap.py +++ b/vicn/resource/linux/macvtap.py @@ -18,14 +18,15 @@ from netmodel.model.type import String from vicn.core.attribute import Attribute -from vicn.core.task import BashTask -from vicn.resource.linux.net_device import SlaveBaseNetDevice +from vicn.core.task import BashTask, inherit +from vicn.resource.interface import Interface +from vicn.resource.linux.net_device import SlaveNetDevice CMD_CREATE_PARENT = 'ip link add name {netdevice.device_name} ' \ 'link {netdevice.parent.device_name} ' \ 'type {netdevice.netdevice_type} mode {netdevice.mode}' -class MacVtap(SlaveBaseNetDevice): +class MacVtap(SlaveNetDevice): """ Resource: MacVtap @@ -48,5 +49,6 @@ class MacVtap(SlaveBaseNetDevice): # Resource lifecycle #-------------------------------------------------------------------------- + @inherit(Interface) def __create__(self): return BashTask(self.node, CMD_CREATE_PARENT, {'netdevice': self}) diff --git a/vicn/resource/linux/net_device.py b/vicn/resource/linux/net_device.py index c393ac1a..f9ab40b1 100644 --- a/vicn/resource/linux/net_device.py +++ b/vicn/resource/linux/net_device.py @@ -23,11 +23,13 @@ import random import string from netmodel.model.type import Integer, String, Bool +from netmodel.model.type import Inet4Address, Inet6Address from vicn.core.address_mgr import AddressManager from vicn.core.attribute import Attribute from vicn.core.exception import ResourceNotFound from vicn.core.resource import BaseResource -from vicn.core.task import BashTask, task, EmptyTask +from vicn.core.task import BashTask, task, EmptyTask, inherit +from vicn.core.task import inherit_parent, override_parent from vicn.resource.linux.application import LinuxApplication as Application from vicn.resource.interface import Interface @@ -59,11 +61,11 @@ CMD_SET_MAC_ADDRESS = 'ip link set dev {netdevice.device_name} ' \ 'address {netdevice.mac_address}' CMD_GET_IP_ADDRESS = 'ip addr show {netdevice.device_name}' CMD_SET_IP4_ADDRESS = 'ip addr add dev {netdevice.device_name} ' \ - '{netdevice.ip4_address} brd + || true' + '{netdevice.ip4_address}/{netdevice.ip4_address.prefix_len} brd + || true' CMD_SET_IP6_ADDRESS = 'ip addr add dev {netdevice.device_name} ' \ - '{netdevice.ip6_address}/{netdevice.ip6_prefix} || true' + '{netdevice.ip6_address}/{netdevice.ip6_address.prefix_len} || true' CMD_SET_PROMISC = 'ip link set dev {netdevice.device_name} promisc {on_off}' -CMD_SET_UP = 'ip link set {netdevice.device_name} {up_down}' +CMD_SET_UP = 'ip link set {netdevice.device_name} {state}' CMD_SET_CAPACITY='\n'.join([ 'tc qdisc del dev {netdevice.device_name} root || true', 'tc qdisc add dev {netdevice.device_name} root handle 1: tbf rate ' @@ -92,6 +94,10 @@ CMD_UNSET_IP6_FWD = 'sysctl -w net.ipv6.conf.{netdevice.device_name}.forwarding= CMD_SET_IP6_FWD = 'sysctl -w net.ipv6.conf.{netdevice.device_name}.forwarding=1' CMD_GET_IP6_FWD = 'sysctl -n net.ipv6.conf.{netdevice.device_name}.forwarding' +DEFAULT_IP4_PREFIX_LEN = 31 +DEFAULT_IP6_PREFIX_LEN = 64 + +NetDeviceName = String.restrict(max_size = MAX_DEVICE_NAME_SIZE) #------------------------------------------------------------------------------- @@ -265,22 +271,20 @@ def parse_ip_addr(data): #------------------------------------------------------------------------------ -class BaseNetDevice(Interface, Application): +class NetDevice(Interface, Application): __type__ = BaseResource # XXX note: ethtool only required if we need to get the pci address __package_names__ = ['ethtool'] - device_name = Attribute(String, description = 'Name of the NetDevice', - default = lambda x : x._default_device_name(), - max_size = MAX_DEVICE_NAME_SIZE) + device_name = Attribute(NetDeviceName, description = 'Name of the NetDevice', + default = lambda x : x._default_device_name()) capacity = Attribute(Integer, - description = 'Capacity for interface shaping (Mb/s)') + description = 'Capacity for interface shaping (Mb/s)', + default = None) mac_address = Attribute(String, description = 'Mac address of the device') - ip4_address = Attribute(String, description = 'IP address of the device') - ip4_prefix = Attribute(Integer, description = 'Prefix for the IPv4link', default=31) #XXX 31? - ip6_address = Attribute(String, description = 'IPv6 address of the device') - ip6_prefix = Attribute(Integer, description = 'Prefix for the IPv6 link', default=64) + ip4_address = Attribute(Inet4Address, description = 'IP address of the device') + ip6_address = Attribute(Inet6Address, description = 'IPv6 address of the device') ip6_forwarding = Attribute(Bool, description = 'IPv6 forwarding', default = True) pci_address = Attribute(String, description = 'PCI bus address of the device', @@ -308,6 +312,7 @@ class BaseNetDevice(Interface, Application): # Resource lifecycle #-------------------------------------------------------------------------- + @inherit_parent def __get__(self): def check(rv): if not bool(rv): @@ -315,8 +320,11 @@ class BaseNetDevice(Interface, Application): return BashTask(self.node, CMD_GET, {'netdevice' : self}, output=True, parse=check) - __create__ = None + @inherit_parent + def __create__(self): + return BashTask(self.node, CMD_CREATE, {'netdevice': self}) + @inherit_parent def __delete__(self): return BashTask(self.node, CMD_DELETE, {'netdevice': self}) @@ -369,7 +377,7 @@ class BaseNetDevice(Interface, Application): if len(ips) > 1: log.warning('Keeping only first of many IP addresses...') ip = ips[0] - attrs['ip4_address'] = ip['ip-address'] + attrs['ip4_address'] = Inet4Address(ip['ip-address'], DEFAULT_IP4_PREFIX_LEN) else: attrs['ip4_address'] = None return attrs @@ -413,8 +421,7 @@ class BaseNetDevice(Interface, Application): if len(ips) > 1: log.warning('Keeping only first of many IPv6 addresses...') ip = ips[0] - attrs['ip6_address'] = ip['ip-address'] - attrs['ip6_prefix'] = ip['prefix'] + attrs['ip6_address'] = Inet6Address(ip_address = ip['ip-address'], prefix_len = ip['prefix']) else: attrs['ip6_address'] = None return attrs @@ -457,9 +464,9 @@ class BaseNetDevice(Interface, Application): return {'up': False} def _set_up(self): - up_down = 'up' if self.up else 'down' + state = 'up' if self.up else 'down' return BashTask(self.node, CMD_SET_UP, - {'netdevice': self, 'up_down': up_down}) + {'netdevice': self, 'state': state}) @task def _get_capacity(self): @@ -526,7 +533,7 @@ class BaseNetDevice(Interface, Application): #------------------------------------------------------------------------------ -class NonTapBaseNetDevice(BaseNetDevice): +class NonTapBaseNetDevice(NetDevice): # Tap devices for instance don't have offload offload = Attribute(Bool, description = 'Offload', default=True) @@ -534,59 +541,34 @@ class NonTapBaseNetDevice(BaseNetDevice): # Attributes #-------------------------------------------------------------------------- - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def _get_offload(self): return BashTask(self.node, CMD_GET_OFFLOAD, {'netdevice': self}, parse = lambda rv : rv.stdout.strip() == 'on') def _set_offload(self): - cmd = None - if self.offload: - cmd = CMD_SET_OFFLOAD - else: - cmd = CMD_UNSET_OFFLOAD + cmd = CMD_SET_OFFLOAD if self.offload else CMD_UNSET_OFFLOAD return BashTask(self.node, cmd, {'netdevice' : self}) #------------------------------------------------------------------------------ -class NetDevice(NonTapBaseNetDevice): - - #-------------------------------------------------------------------------- - # Resource lifecycle - #-------------------------------------------------------------------------- - - def __create__(self): - return BashTask(self.node, CMD_CREATE, {'netdevice': self}) - -#------------------------------------------------------------------------------ - -class SlaveBaseNetDevice(BaseNetDevice): +class SlaveNetDevice(NetDevice): parent = Attribute(NetDevice, description = 'Parent NetDevice') - host = Attribute(NetDevice, description = 'Host interface', - default = lambda x : x._default_host()) - - def _default_host(self): - if self.node.__class__.__name__ == 'LxcContainer': - host = self.node.node - else: - host = self.node - max_len = MAX_DEVICE_NAME_SIZE - len(self.node.name) - 1 - device_name = self.device_name[:max_len] - - return NetDevice(node = host, - device_name = '{}-{}'.format(self.node.name, device_name), - managed = False) - -#------------------------------------------------------------------------------ - -class SlaveNetDevice(SlaveBaseNetDevice): - - #-------------------------------------------------------------------------- - # Resource lifecycle - #-------------------------------------------------------------------------- +# host = Attribute(NetDevice, description = 'Host interface', +# default = lambda x : x._default_host()) +# +# def _default_host(self): +# if self.node.__class__.__name__ == 'LxcContainer': +# host = self.node.node +# else: +# host = self.node +# max_len = MAX_DEVICE_NAME_SIZE - len(self.node.name) - 1 +# device_name = self.device_name[:max_len] +# +# return NetDevice(node = host, +# device_name = '{}-{}'.format(self.node.name, device_name), +# managed = False) + @override_parent def __create__(self): return BashTask(self.node, CMD_CREATE_PARENT, {'netdevice': self}) diff --git a/vicn/resource/linux/netmon.py b/vicn/resource/linux/netmon.py index 8472f308..9c5c97fd 100644 --- a/vicn/resource/linux/netmon.py +++ b/vicn/resource/linux/netmon.py @@ -25,5 +25,5 @@ class NetMon(Service): Generic network monitoring daemon, used internally by VICN for resource monitoring. """ - __package_names__ = ['netmon'] + __package_names__ = [] # XXX transition 'netmon'] __service_name__ = 'netmon' diff --git a/vicn/resource/linux/ovs.py b/vicn/resource/linux/ovs.py index d67e4bca..c0b57151 100644 --- a/vicn/resource/linux/ovs.py +++ b/vicn/resource/linux/ovs.py @@ -46,12 +46,12 @@ class OVS(BridgeManager): #--------------------------------------------------------------------------- def add_bridge(self, bridge_name): - return BashTask(self.node, CMD_ADD_BRIDGE, + return BashTask(self.node, CMD_ADD_BRIDGE, {'bridge_name': bridge_name}, output = False, as_root = True) def del_bridge(self, bridge_name): - return BashTask(self.node, CMD_DEL_BRIDGE, + return BashTask(self.node, CMD_DEL_BRIDGE, {'bridge_name': bridge_name}, output = False, as_root = True) @@ -62,7 +62,7 @@ class OVS(BridgeManager): output = False, as_root = True) def del_interface(self, bridge_name, interface_name, vlan=None): - return BashTask(self.node, CMD_DEL_INTERFACE, + return BashTask(self.node, CMD_DEL_INTERFACE, {'bridge_name': bridge_name, 'interface_name': interface_name, 'vlan': vlan}, output = False, as_root = True) diff --git a/vicn/resource/linux/package_manager.py b/vicn/resource/linux/package_manager.py index 93241502..04a47986 100644 --- a/vicn/resource/linux/package_manager.py +++ b/vicn/resource/linux/package_manager.py @@ -19,13 +19,14 @@ import asyncio import logging +from netmodel.model.key import Key from netmodel.model.type import String, Bool from vicn.core.attribute import Attribute, Multiplicity from vicn.core.exception import ResourceNotFound from vicn.core.requirement import Requirement from vicn.core.resource import Resource from vicn.core.task import BashTask, EmptyTask, async_task -from vicn.core.task import inline_task, run_task +from vicn.core.task import inline_task, run_task, inherit_parent from vicn.resource.node import Node log = logging.getLogger(__name__) @@ -78,12 +79,13 @@ class PackageManager(Resource): reverse_name = 'package_manager', reverse_auto = True, mandatory = True, - key = True, multiplicity = Multiplicity.OneToOne) trusted = Attribute(Bool, description="Force repository trust", default=False) + __key__ = Key(node) + #-------------------------------------------------------------------------- # Constructor and Accessors #-------------------------------------------------------------------------- @@ -100,6 +102,7 @@ class PackageManager(Resource): def __after__(self): return ('Repository',) + @inherit_parent @inline_task def __get__(self): raise ResourceNotFound @@ -182,21 +185,25 @@ class Package(Resource): package_name = Attribute(String, mandatory = True) node = Attribute(Node, mandatory = True, - key = True, requirements=[ Requirement('package_manager') ]) + __key__ = Key(node) + #--------------------------------------------------------------------------- # Resource lifecycle #--------------------------------------------------------------------------- + @inherit_parent def __get__(self): return BashTask(self.node, CMD_PKG_TEST, {'self': self}) + @inherit_parent def __create__(self): return self.node.package_manager.__method_install__(self.package_name) + @inherit_parent @async_task async def __delete__(self): with await self.node.package_manager._lock: @@ -218,15 +225,17 @@ class Packages(Resource): names = Attribute(String, multiplicity = Multiplicity.OneToMany) node = Attribute(Node, mandatory = True, - key = True, requirements=[ Requirement('package_manager') ]) + __key__ = Key(node) + #--------------------------------------------------------------------------- # Resource lifecycle #--------------------------------------------------------------------------- + @inherit_parent def __subresources__(self): """ Note: Although packages are (rightfully) specified concurrent, apt tasks diff --git a/vicn/resource/linux/phy_interface.py b/vicn/resource/linux/phy_interface.py index 81d2950c..8d7f02c8 100644 --- a/vicn/resource/linux/phy_interface.py +++ b/vicn/resource/linux/phy_interface.py @@ -16,7 +16,8 @@ # limitations under the License. # -from netmodel.model.type import String +from netmodel.model.type import String, Integer +from netmodel.model.type import Inet4Address, Inet6Address from vicn.core.attribute import Attribute from vicn.core.resource import BaseResource from vicn.resource.interface import Interface @@ -34,17 +35,6 @@ class PhyInterface(Interface): mandatory = True) pci_address = Attribute(String, description = "Device's PCI bus address", mandatory = True) - mac_address = Attribute(String, description = "Device's MAC address", - mandatory=True) - ip4_address = Attribute(String, description = "Device's IP address") - ip6_address = Attribute(String, description = "Device's IP address") - - #-------------------------------------------------------------------------- - # Constructor and Accessors - #-------------------------------------------------------------------------- - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if not self.name: - self.name = self.node.name + '-' + self.device_name + mac_address = Attribute(String, description = "Device's MAC address") + ip4_address = Attribute(Inet4Address, description = "Device's IP address") + ip6_address = Attribute(Inet6Address, description = "Device's IP address") diff --git a/vicn/resource/linux/phy_link.py b/vicn/resource/linux/phy_link.py index 878cf7c6..2976b7f0 100644 --- a/vicn/resource/linux/phy_link.py +++ b/vicn/resource/linux/phy_link.py @@ -16,10 +16,12 @@ # limitations under the License. # -from vicn.core.attribute import Attribute -from vicn.core.task import inline_task +from vicn.core.attribute import Attribute, Reference +from vicn.core.task import inline_task, async_task +from vicn.core.task import inherit_parent from vicn.resource.channel import Channel from vicn.resource.linux.phy_interface import PhyInterface +from vicn.resource.vpp.interface import VPPInterface class PhyLink(Channel): """ @@ -28,12 +30,53 @@ class PhyLink(Channel): Physical Link to inform the orchestrator about Layer2 connectivity. """ - src = Attribute(PhyInterface, description = 'Source interface', + src = Attribute(PhyInterface, description = 'Source interface', mandatory = True) - dst = Attribute(PhyInterface, description = 'Destination interface', + dst = Attribute(PhyInterface, description = 'Destination interface', mandatory = True) + @inherit_parent @inline_task def __initialize__(self): self.src.set('channel', self) self.dst.set('channel', self) + + #-------------------------------------------------------------------------- + # Internal methods + #-------------------------------------------------------------------------- + + async def _commit(self): + manager = self._state.manager + + # Disable rp_filtering + # self.src.rp_filter = False + # self.dst.rp_filter = False + + #XXX VPP + vpp_src = VPPInterface(parent = self.src, + vpp = self.src.node.vpp, + ip4_address = Reference(self.src, 'ip4_address'), + ip6_address = Reference(self.src, 'ip6_address'), + device_name = self.src.device_name) + manager.commit_resource(vpp_src) + + + vpp_dst = VPPInterface(parent = self.dst, + vpp = self.dst.node.vpp, + ip4_address = Reference(self.dst, 'ip4_address'), + ip6_address = Reference(self.dst, 'ip6_address'), + device_name = self.dst.device_name) + manager.commit_resource(vpp_dst) + + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + + def __get__(self): + return async_task(self._commit)() + + def __create__(self): + assert self.src.node.get_type() == 'lxccontainer' + assert self.dst.node.get_type() == 'lxccontainer' + + return async_task(self._commit)() diff --git a/vicn/resource/linux/physical.py b/vicn/resource/linux/physical.py index f71b5856..c058ff16 100644 --- a/vicn/resource/linux/physical.py +++ b/vicn/resource/linux/physical.py @@ -29,6 +29,7 @@ from vicn.core.attribute import Attribute from vicn.core.commands import Command, ReturnValue from vicn.core.exception import ResourceNotFound, VICNException from vicn.core.task import Task, task, EmptyTask +from vicn.core.task import inherit_parent from vicn.resource.linux.keypair import Keypair from vicn.resource.node import Node, DEFAULT_USERNAME from vicn.resource.node import DEFAULT_SSH_PUBLIC_KEY @@ -71,14 +72,16 @@ class Physical(Node): # Resource lifecycle #-------------------------------------------------------------------------- + @inherit_parent def __subresources__(self): """ Require a SSH keypair to be present for authentication on nodes """ return Keypair(node = self, key = FN_KEY) + @inherit_parent def __initialize__(self): - if not is_local_host(self.hostname): + if self.managed and not is_local_host(self.hostname): """ Initialization require the ssh port to be open on the node, and the ssh public key to be copied on the remote node. @@ -124,6 +127,7 @@ class Physical(Node): p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) if output: out, err = p.communicate() + #print('error {}, output {}'.format(err,out)) return ReturnValue(p.returncode, stdout=out, stderr=err) p.wait() diff --git a/vicn/resource/linux/qtplayer.py b/vicn/resource/linux/qtplayer.py new file mode 100644 index 00000000..44b10ccf --- /dev/null +++ b/vicn/resource/linux/qtplayer.py @@ -0,0 +1,5 @@ +from vicn.resource.linux.application import LinuxApplication + +class QtPlayer(LinuxApplication): + pass + diff --git a/vicn/resource/linux/repository.py b/vicn/resource/linux/repository.py index cd740d38..f07421ba 100644 --- a/vicn/resource/linux/repository.py +++ b/vicn/resource/linux/repository.py @@ -31,14 +31,14 @@ class Repository(Application): part of any basic distribution install. """ - repo_name = Attribute(String, description = 'Name of the repository', + repo_name = Attribute(String, description = 'Name of the repository', default = 'vicn') - directory = Attribute(String, description = 'Directory holding packages', + directory = Attribute(String, description = 'Directory holding packages', default = '') sections = Attribute(String, description = 'Sections', multiplicity = Multiplicity.OneToMany, default = []) - distributions = Attribute(String, + distributions = Attribute(String, description = 'List of distributions served by this repository', multiplicity = Multiplicity.ManyToMany, default = ['sid', 'trusty', 'xenial']) diff --git a/vicn/resource/linux/service.py b/vicn/resource/linux/service.py index 3eb753fc..1143461a 100644 --- a/vicn/resource/linux/service.py +++ b/vicn/resource/linux/service.py @@ -21,6 +21,7 @@ import logging from vicn.core.exception import ResourceNotFound from vicn.core.resource import CategoryResource from vicn.core.task import inline_task, BashTask, EmptyTask +from vicn.core.task import inherit_parent from vicn.resource.linux.application import LinuxApplication log = logging.getLogger(__name__) @@ -51,33 +52,33 @@ class Service(LinuxApplication): __type__ = CategoryResource - + @inherit_parent @inline_task def __get__(self): raise ResourceNotFound - + def __method_restart__(self): - return BashTask(self.node, CMD_RESTART, + return BashTask(self.node, CMD_RESTART, {'service_name': self.__service_name__}) - + def __method_start__(self): return BashTask(self.node, CMD_START, {'service_name': self.__service_name__}) - - def __create__(self): - if self.__service_name__ == 'lxd': - log.warning('Not restarting LXD') - return EmptyTask() + def __method_stop__(self): + return BashTask(self.node, CMD_STOP, + {'service_name': self.__service_name__}) - if self.__service_name__ == 'dnsmasq': - return BashTask(self.node, CMD_STOP_START, - {'service_name': self.__service_name__}) + def __method_stop_start(self): + return BashTask(self.node, CMD_STOP_START, + {'service_name': self.__service_name__}) + @inherit_parent + def __create__(self): return self.__method_restart__() - + @inherit_parent def __delete__(self): - return BashTask(self.node, CMD_STOP, + return BashTask(self.node, CMD_STOP, {'service_name': self.__service_name__}) diff --git a/vicn/resource/linux/sym_veth_pair.py b/vicn/resource/linux/sym_veth_pair.py index bf79a69b..ebfe2968 100644 --- a/vicn/resource/linux/sym_veth_pair.py +++ b/vicn/resource/linux/sym_veth_pair.py @@ -26,12 +26,12 @@ from vicn.core.resource import Resource from vicn.core.state import ResourceState, AttributeState from vicn.core.task import BashTask, get_attributes_task from vicn.core.task import async_task, task, inline_task -from vicn.core.task import run_task +from vicn.core.task import run_task, inherit_parent from vicn.resource.interface import Interface from vicn.resource.node import Node from vicn.resource.linux.net_device import NonTapBaseNetDevice from vicn.resource.linux.link import CMD_DELETE_IF_EXISTS -from vicn.resource.linux.link import CMD_UP +from vicn.resource.linux.link import CMD_SET_UP CMD_CREATE=''' # Create veth pair in the host node @@ -48,13 +48,13 @@ class SymVethPair(Resource): resource is that is it not a channel. """ - node1 = Attribute(Node, + node1 = Attribute(Node, description = 'Node on which one side of the veth will sit', mandatory = True) - node2 = Attribute(Node, + node2 = Attribute(Node, description = 'Node on which the other side of the veth will sit', mandatory = True) - capacity = Attribute(Integer, + capacity = Attribute(Integer, description = 'Capacity of the veth pair (Mb/s)') side1 = Attribute(Interface, description = 'Source interface') side2 = Attribute(Interface, description = 'Destination interface') @@ -66,21 +66,22 @@ class SymVethPair(Resource): async def _commit(self): # see link.py for explanations manager = self._state.manager - await manager._set_resource_state(self.side1, + await manager._set_resource_state(self.side1, ResourceState.INITIALIZED) - await manager._set_resource_state(self.side2, + await manager._set_resource_state(self.side2, ResourceState.INITIALIZED) await manager._set_resource_state(self.side1, ResourceState.CREATED) await manager._set_resource_state(self.side2, ResourceState.CREATED) await manager._set_attribute_state(self, 'side1', AttributeState.CLEAN) await manager._set_attribute_state(self, 'side2', AttributeState.CLEAN) manager.commit_resource(self.side1) - manager.commit_resource(self.side2) + manager.commit_resource(self.side2) #-------------------------------------------------------------------------- # Resource lifecycle #-------------------------------------------------------------------------- + @inherit_parent @inline_task def __initialize__(self): self.side1 = NonTapBaseNetDevice(node = self.node1, @@ -94,6 +95,7 @@ class SymVethPair(Resource): self.side1.remote = self.side2 self.side2.remote = self.side1 + @inherit_parent @async_task async def __get__(self): manager = self._state.manager @@ -106,46 +108,48 @@ class SymVethPair(Resource): await self._commit() + @inherit_parent def __create__(self): assert self.node1.get_type() == 'lxccontainer' assert self.node2.get_type() == 'lxccontainer' - + node1_host = self.node1.node node2_host = self.node2.node - + assert node1_host == node2_host host = node1_host - + # Sometimes a down interface persists on one side - delif_side1 = BashTask(self.node1, CMD_DELETE_IF_EXISTS, + delif_side1 = BashTask(self.node1, CMD_DELETE_IF_EXISTS, {'interface': self.side1}) - delif_side2 = BashTask(self.node2, CMD_DELETE_IF_EXISTS, + delif_side2 = BashTask(self.node2, CMD_DELETE_IF_EXISTS, {'interface': self.side2}) - + pid_node1 = get_attributes_task(self.node1, ['pid']) pid_node2 = get_attributes_task(self.node2, ['pid']) - + tmp_side1 = 'tmp-veth-' + ''.join(random.choice( string.ascii_uppercase + string.digits) for _ in range(5)) tmp_side2 = 'tmp-veth-' + ''.join(random.choice( string.ascii_uppercase + string.digits) for _ in range(5)) - - create = BashTask(host, CMD_CREATE, + + create = BashTask(host, CMD_CREATE, {'side1_device_name': self.side1.device_name, - 'side2_device_name': self.side2.device_name, + 'side2_device_name': self.side2.device_name, 'tmp_side1': tmp_side1, 'tmp_side2': tmp_side2}) - - up_side1 = BashTask(self.node1, CMD_UP, {'interface': self.side1}) - up_side2 = BashTask(self.node2, CMD_UP, {'interface': self.side2}) - + + up_side1 = BashTask(self.node1, CMD_SET_UP, {'interface': self.side1}) + up_side2 = BashTask(self.node2, CMD_SET_UP, {'interface': self.side2}) + @async_task async def set_state(): await self._commit() - + delif = delif_side1 | delif_side2 up = up_side1 | up_side2 pid = pid_node1 | pid_node2 return ((delif > (pid @ create)) > up) > set_state() - + + @inherit_parent def __delete__(self): raise NotImplementedError diff --git a/vicn/resource/linux/tap_device.py b/vicn/resource/linux/tap_device.py index b7c9f967..88ca055d 100644 --- a/vicn/resource/linux/tap_device.py +++ b/vicn/resource/linux/tap_device.py @@ -18,39 +18,22 @@ from netmodel.model.type import String from vicn.core.attribute import Attribute -from vicn.core.task import BashTask -from vicn.resource.linux.net_device import BaseNetDevice, IPV4, IPV6, CMD_FLUSH_IP +from vicn.core.task import BashTask, override_parent +from vicn.resource.linux.net_device import NetDevice, IPV4, IPV6, CMD_FLUSH_IP CMD_CREATE='ip tuntap add name {netdevice.device_name} mode tap' -#CMD_SET_IP_ADDRESS='ip -{version} addr add dev {netdevice.device_name} 0.0.0.0' -class TapDevice(BaseNetDevice): +class TapDevice(NetDevice): def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) self.prefix = 'tap' self.netdevice_type = 'tap' + @override_parent def __create__(self): return BashTask(self.node, CMD_CREATE, {'netdevice': self}) -##mengueha: do we actually need that? -# def _set_ip4_address(self): -# if self.ip4_address is None: -# # Unset IP -# return BashTask(self.node, CMD_FLUSH_IP, -# {'device_name': self.device_name}) -# return BashTask(self.node, CMD_SET_IP_ADDRESS, -# {'netdevice': self}) -# -# def _set_ip6_address(self): -# if self.ip6_address is None: -# # Unset IP -# return BashTask(self.node, CMD_FLUSH_IP, -# {'ip_version': IPV6, 'device_name': self.device_name}) -# return BashTask(self.node, CMD_SET_IP_ADDRESS, -# {'netdevice': self}) - class TapChannel(TapDevice): station_name = Attribute(String) channel_name = Attribute(String) diff --git a/vicn/resource/linux/veth_pair.py b/vicn/resource/linux/veth_pair.py index 53fa9bf8..52050074 100644 --- a/vicn/resource/linux/veth_pair.py +++ b/vicn/resource/linux/veth_pair.py @@ -15,48 +15,82 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import random -import string -from vicn.resource.linux.net_device import SlaveBaseNetDevice -from vicn.core.task import BashTask, get_attributes_task - -# ip link add veth0 type veth peer name veth1 +from netmodel.model.key import Key +from netmodel.model.type import String, Bool +from vicn.core.attribute import Attribute, Reference +from vicn.core.task import BashTask, inline_task, get_attributes_task +from vicn.resource.linux.net_device import NetDevice, NetDeviceName +from vicn.resource.node import Node +from vicn.resource.symmetric_channel import SymmetricChannel CMD_CREATE=''' # Create veth pair in the host node -ip link add name {interface.host.device_name} type veth peer name {tmp_name} +ip link add name {self.device_name} type veth peer name {self.peer_device_name} +''' + +DEPRECATED_CMD_UP=''' # The host interface will always be up... ip link set dev {interface.host.device_name} up + # Move interface into container and rename it ip link set dev {tmp_name} netns {pid} name {interface.device_name} -''' -CMD_UP=''' ip link set dev {interface.device_name} up ''' -# see: -# http://stackoverflow.com/questions/22780927/lxc-linux-containers-add-new-network-interface-without-restarting +# Forward declaration +class VethPair(SymmetricChannel): + pass + +class VethNetDevice(NetDevice): + parent = Attribute(VethPair, mandatory = True, ro = True) -class VethPair(SlaveBaseNetDevice): - # Do not need the parent attribute... + __get__ = None + __create__ = None + __delete__ = None - def __init__(self, *args, **kwargs): - super().__init__(self, *args, **kwargs) - self.prefix = 'veth' - self.netdevice_type = 'veth' +class VethPair(SymmetricChannel): + # Mimics NetDevice for using its __get__ and __delete__ functions + node = Attribute(Node) + device_name = Attribute(NetDeviceName) + peer_device_name = Attribute(NetDeviceName) + capacity = Attribute(String) + src = Attribute(ro = True, mandatory = False) + dst = Attribute(ro = True, mandatory = False) + auto_commit = Attribute(Bool, description = 'Auto commit interfaces') + + __key1__ = Key(node, device_name) + __key2__ = Key(node, peer_device_name) + + @inline_task + def _commit(self): + if self.auto_commit: + manager = self._state.manager + + manager.commit_resource(self.src) + manager.commit_resource(self.dst) + + def __initialize__(self): + # XXX owner prevents the resource to be committed + self.src = VethNetDevice(node = self.node, + parent = self, + device_name = self.device_name, + channel = self, + capacity = Reference(self, 'capacity'), + owner = self) + self.dst = VethNetDevice(node = self.node, + parent = self, + device_name = self.peer_device_name, + channel = self, + capacity = Reference(self, 'capacity'), + owner = self) def __create__(self): - assert self.node.__class__.__name__ == 'LxcContainer' - host = self.node.node - pid = get_attributes_task(self.node, ['pid']) - tmp_name = 'tmp-veth-' + ''.join(random.choice(string.ascii_uppercase \ - + string.digits) for _ in range(5)) - create = BashTask(host, CMD_CREATE, {'tmp_name': tmp_name, - 'interface': self}) - up = BashTask(self.node, CMD_UP, {'interface': self}) - bridge = host.bridge_manager.add_interface(host.bridge.device_name, - self.host.device_name) - return ((pid @ create) > up) > bridge - - # ... IP and UP missing... + veth = BashTask(self.node, CMD_CREATE, {'self': self}) + return (veth > super().__create__()) > self._commit() + + def __get__(self): + return NetDevice.__get__(self) > self._commit() + + def __delete__(self): + return NetDevice.__delete__(self) diff --git a/vicn/resource/linux/veth_pair_lxc.py b/vicn/resource/linux/veth_pair_lxc.py new file mode 100644 index 00000000..dd26b7bb --- /dev/null +++ b/vicn/resource/linux/veth_pair_lxc.py @@ -0,0 +1,74 @@ +#!/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. +# +import random +import string + +from vicn.core.attribute import Attribute +from vicn.resource.linux.net_device import NetDevice, SlaveNetDevice +from vicn.core.task import BashTask, get_attributes_task +from vicn.core.task import override_parent +from vicn.core.attribute import Attribute + +# ip link add veth0 type veth peer name veth1 + +CMD_CREATE=''' +# Create veth pair in the host node +ip link add name {interface.host.device_name} type veth peer name {tmp_name} +# The host interface will always be up... +ip link set dev {interface.host.device_name} up +# Move interface into container and rename it +ip link set dev {tmp_name} netns {pid} name {interface.device_name} +''' +CMD_UP=''' +ip link set dev {interface.device_name} up +''' + +# see: +# http://stackoverflow.com/questions/22780927/lxc-linux-containers-add-new-network-interface-without-restarting + +class VethPairLxc(SlaveNetDevice): + + host = Attribute(NetDevice, description = 'Host interface', + default = lambda x : x._default_host()) + + def _default_host(self): + if self.node.__class__.__name__ == 'LxcContainer': + host = self.node.node + else: + host = self.node + max_len = MAX_DEVICE_NAME_SIZE - len(self.node.name) - 1 + device_name = self.device_name[:max_len] + + return NetDevice(node = host, + device_name = '{}-{}'.format(self.node.name, device_name), + managed = False) + + def __create__(self): + assert self.node.__class__.__name__ == 'LxcContainer' + host = self.node.node + pid = get_attributes_task(self.node, ['pid']) + tmp_name = 'tmp-veth-' + ''.join(random.choice(string.ascii_uppercase \ + + string.digits) for _ in range(5)) + create = BashTask(host, CMD_CREATE, {'tmp_name': tmp_name, + 'interface': self}) + up = BashTask(self.node, CMD_UP, {'interface': self}) + bridge = host.bridge_manager.add_interface(host.bridge.device_name, + self.host.device_name) + return ((pid @ create) > up) > bridge + + # ... IP and UP missing... |