diff options
Diffstat (limited to 'vicn/resource/linux')
-rw-r--r-- | vicn/resource/linux/certificate.py | 9 | ||||
-rw-r--r-- | vicn/resource/linux/keypair.py | 71 | ||||
-rw-r--r-- | vicn/resource/linux/link.py | 75 | ||||
-rw-r--r-- | vicn/resource/linux/net_device.py | 3 | ||||
-rw-r--r-- | vicn/resource/linux/physical.py | 40 |
5 files changed, 127 insertions, 71 deletions
diff --git a/vicn/resource/linux/certificate.py b/vicn/resource/linux/certificate.py index e8750dff..7f9b8a74 100644 --- a/vicn/resource/linux/certificate.py +++ b/vicn/resource/linux/certificate.py @@ -31,6 +31,8 @@ DEFAULT_SUBJECT = '/CN=www.cisco.com/L=Paris/O=Cisco/C=FR' CMD_CREATE='\n'.join([ '# Generate a new certificate', + 'mkdir -p $(dirname {self.key})', + 'mkdir -p $(dirname {self.cert})', 'openssl req -x509 -newkey rsa:' + DEFAULT_RSA_LENGTH + ' -keyout {self.key} -out {self.cert} -subj ' + DEFAULT_SUBJECT + ' -nodes' ]) @@ -40,9 +42,6 @@ class Certificate(Resource): Resource: Certificate Implements a SSL certificate. - - Todo: - - ideally, this should be implemented as a pair of tightly coupled files. """ node = Attribute(Node, description = 'Node on which the certificate is created', @@ -53,6 +52,10 @@ class Certificate(Resource): key = Attribute(String, description = 'Key path', mandatory = True) + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + @inline_task def __initialize__(self): self._cert_file = File(node = Reference(self, 'node'), diff --git a/vicn/resource/linux/keypair.py b/vicn/resource/linux/keypair.py new file mode 100644 index 00000000..a81a40d4 --- /dev/null +++ b/vicn/resource/linux/keypair.py @@ -0,0 +1,71 @@ +#!/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 os.path + +from netmodel.model.type import String +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.resource.linux.file import File +from vicn.resource.node import Node + +CMD_CREATE=''' +mkdir -p {dirname} +ssh-keygen -t rsa -N "" -f {self.key} +''' + +class Keypair(Resource): + """ + Resource: Keypair + + Implements a SSH keypair + """ + node = Attribute(Node, + description = 'Node on which the certificate is created', + mandatory = True, + multiplicity = Multiplicity.ManyToOne) + key = Attribute(String, description = 'Key path', + mandatory = True) + + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + + @inline_task + def __initialize__(self): + self._pubkey_file = File(node = Reference(self, 'node'), + filename = self.key + '.pub', + managed = False) + self._key_file = File(node = Reference(self, 'node'), + filename = self.key, + managed = False) + + def __get__(self): + return self._pubkey_file.__get__() | self._key_file.__get__() + + def __create__(self): + return BashTask(None, CMD_CREATE, { + 'dirname': os.path.dirname(self.key), + 'self': self}) + + 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 4304a948..a4771f9c 100644 --- a/vicn/resource/linux/link.py +++ b/vicn/resource/linux/link.py @@ -42,8 +42,8 @@ CMD_DELETE_IF_EXISTS='ip link show {interface.device_name} && ' \ CMD_CREATE=''' # Create veth pair in the host node ip link add name {tmp_src} type veth peer name {tmp_dst} -ip link set dev {tmp_src} netns {pid[0]} name {interface.src.device_name} -ip link set dev {tmp_dst} netns {pid[1]} name {interface.dst.device_name} +ip link set dev {tmp_src} netns {pid[0]} name {interface._src.device_name} +ip link set dev {tmp_dst} netns {pid[1]} name {interface._dst.device_name} ''' CMD_UP=''' ip link set dev {interface.device_name} up @@ -61,9 +61,6 @@ class Link(Channel): the current implementation. """ - src = Attribute(Interface, description = 'Source interface') - dst = Attribute(Interface, description = 'Destination interface') - capacity = Attribute(Integer, description = 'Link capacity (Mb/s)') delay = Attribute(String, description = 'Link propagation delay') @@ -73,29 +70,29 @@ class Link(Channel): mandatory = True) def __init__(self, *args, **kwargs): - assert 'src' not in kwargs and 'dst' not in kwargs assert 'src_node' in kwargs and 'dst_node' in kwargs + self._src = None + self._dst = None super().__init__(*args, **kwargs) @inline_task def __initialize__(self): - # We create two managed net devices that are pre-setup # but the resource manager has to take over for IP addresses etc. # Being done in initialize, those attributes won't be considered as # dependencies and will thus not block the resource state machine. - self.src = NonTapBaseNetDevice(node = self.src_node, + self._src = NonTapBaseNetDevice(node = self.src_node, device_name = self.dst_node.name, channel = self, capacity = self.capacity, - owner = self.owner) - self.dst = NonTapBaseNetDevice(node = self.dst_node, + owner = self) + self._dst = NonTapBaseNetDevice(node = self.dst_node, device_name = self.src_node.name, channel = self, capacity = self.capacity, - owner = self.owner) - self.dst.remote = self.src - self.src.remote = self.dst + owner = self) + self._dst.remote = self._src + self._src.remote = self._dst #-------------------------------------------------------------------------- # Internal methods @@ -104,21 +101,8 @@ class Link(Channel): async def _commit(self): manager = self._state.manager - # We mark the src and dst interfaces created because we are pre-setting - # them up in __create__ using a VethPair - # We go through both INITIALIZED and CREATED stats to raise the proper - # events and satisfy any eventual wait_* command. - await manager._set_resource_state(self.src, ResourceState.INITIALIZED) - await manager._set_resource_state(self.dst, ResourceState.INITIALIZED) - await manager._set_resource_state(self.src, ResourceState.CREATED) - await manager._set_resource_state(self.dst, ResourceState.CREATED) - - # We mark the attribute clean so that it is not updated - await manager._set_attribute_state(self, 'src', AttributeState.CLEAN) - await manager._set_attribute_state(self, 'dst', AttributeState.CLEAN) - - manager.commit_resource(self.src) - manager.commit_resource(self.dst) + manager.commit_resource(self._src) + manager.commit_resource(self._dst) # Disable rp_filtering # self.src.rp_filter = False @@ -143,36 +127,23 @@ class Link(Channel): # Resource lifecycle #-------------------------------------------------------------------------- - @async_task - async def __get__(self): - manager = self._state.manager + def __get__(self): + return (self._src.__get__() | self._dst.__get__()) > async_task(self._commit)() - try: - await run_task(self.src.__get__(), manager) - await run_task(self.dst.__get__(), manager) - except ResourceNotFound: - # This is raised if any of the two side of the VethPair is missing - raise ResourceNotFound - - # We always need to commit the two endpoints so that their attributes - # are correctly updated - await self._commit() - def __create__(self): assert self.src_node.get_type() == 'lxccontainer' assert self.dst_node.get_type() == 'lxccontainer' src_host = self.src_node.node dst_host = self.dst_node.node - assert src_host == dst_host host = src_host # Sometimes a down interface persists on one side delif_src = BashTask(self.src_node, CMD_DELETE_IF_EXISTS, - {'interface': self.src}) + {'interface': self._src}) delif_dst = BashTask(self.dst_node, CMD_DELETE_IF_EXISTS, - {'interface': self.dst}) + {'interface': self._dst}) pid_src = get_attributes_task(self.src_node, ['pid']) pid_dst = get_attributes_task(self.dst_node, ['pid']) @@ -185,17 +156,13 @@ class Link(Channel): 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}) - - @async_task - async def set_state(): - # We always need to commit the two endpoints so that their attributes - # are correctly updated - await self._commit() + up_src = BashTask(self.src_node, CMD_UP, {'interface': self._src}) + up_dst = BashTask(self.dst_node, CMD_UP, {'interface': self._dst}) delif = delif_src | delif_dst up = up_src | up_dst pid = pid_src | pid_dst - return ((delif > (pid @ create)) > up) > set_state() + return ((delif > (pid @ create)) > up) > async_task(self._commit)() + def __delete__(self): + return self._src.__delete__() | self._dst.__delete__() diff --git a/vicn/resource/linux/net_device.py b/vicn/resource/linux/net_device.py index f0a08991..84a946a4 100644 --- a/vicn/resource/linux/net_device.py +++ b/vicn/resource/linux/net_device.py @@ -464,6 +464,9 @@ 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') diff --git a/vicn/resource/linux/physical.py b/vicn/resource/linux/physical.py index e5eba2d3..8decb517 100644 --- a/vicn/resource/linux/physical.py +++ b/vicn/resource/linux/physical.py @@ -22,16 +22,17 @@ import logging import subprocess import shlex -from netmodel.model.type import String, Integer -from netmodel.util.misc import is_local_host -from netmodel.util.socket import check_port -from vicn.core.attribute import Attribute -from vicn.core.commands import Command, ReturnValue -from vicn.core.exception import ResourceNotFound -from vicn.core.task import Task, task -from vicn.resource.node import Node, DEFAULT_USERNAME -from vicn.resource.node import DEFAULT_SSH_PUBLIC_KEY -from vicn.resource.node import DEFAULT_SSH_PRIVATE_KEY +from netmodel.model.type import String, Integer +from netmodel.util.misc import is_local_host +from netmodel.util.socket import check_port +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 +from vicn.resource.linux.keypair import Keypair +from vicn.resource.node import Node, DEFAULT_USERNAME +from vicn.resource.node import DEFAULT_SSH_PUBLIC_KEY +from vicn.resource.node import DEFAULT_SSH_PRIVATE_KEY log = logging.getLogger(__name__) @@ -42,6 +43,9 @@ CMD_SSH = 'ssh {ssh_options} -i {private_key} -p {port} ' \ CMD_SSH_NF = 'ssh -n -f {ssh_options} -i {private_key} -p {port} ' \ '{user}@{host} {command}' +FN_KEY = os.path.expanduser(os.path.join( + '~', '.vicn', 'ssh_client_cert', 'ssh_client_key')) + class Physical(Node): """ Resource: Physical @@ -67,12 +71,20 @@ class Physical(Node): # Resource lifecycle #-------------------------------------------------------------------------- - @task - def __get__(self, attributes=None): + def __subresources__(self): + """ + Require a SSH keypair to be present for authentication on nodes + """ + return Keypair(node = None, key = FN_KEY) + + def __initialize__(self): + """ + Initialization require the ssh port to be open on the node, and the ssh + public key to be copied on the remote node. + """ if not check_port(self.hostname, self.ssh_port): - raise ResourceNotFound + raise VICNException - def __create__(self): tasks = list() modes = (True, False) if DEFAULT_USERNAME != 'root' else (True,) for as_root in modes: |