summaryrefslogtreecommitdiffstats
path: root/vicn/resource/linux
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/linux')
-rw-r--r--vicn/resource/linux/certificate.py9
-rw-r--r--vicn/resource/linux/keypair.py71
-rw-r--r--vicn/resource/linux/link.py75
-rw-r--r--vicn/resource/linux/net_device.py3
-rw-r--r--vicn/resource/linux/physical.py40
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: