summaryrefslogtreecommitdiffstats
path: root/vicn/resource/ns3/emulated_channel.py
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/ns3/emulated_channel.py')
-rw-r--r--vicn/resource/ns3/emulated_channel.py209
1 files changed, 209 insertions, 0 deletions
diff --git a/vicn/resource/ns3/emulated_channel.py b/vicn/resource/ns3/emulated_channel.py
new file mode 100644
index 00000000..08d7a14b
--- /dev/null
+++ b/vicn/resource/ns3/emulated_channel.py
@@ -0,0 +1,209 @@
+#!/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 logging
+import random
+
+from netmodel.model.type import Integer
+from netmodel.util.socket import check_port
+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.resource import BaseResource
+from vicn.core.resource_mgr import wait_resources
+from vicn.core.task import inline_task, async_task, task
+from vicn.core.task import BashTask, run_task
+from vicn.resource.channel import Channel
+from vicn.resource.linux.application import LinuxApplication as Application
+from vicn.resource.linux.net_device import NetDevice
+from vicn.resource.linux.tap_device import TapDevice
+from vicn.resource.linux.veth_pair import VethPair
+from vicn.resource.lxd.lxc_container import LxcContainer
+from vicn.resource.node import Node
+
+log = logging.getLogger(__name__)
+
+class EmulatedChannel(Channel, Application):
+ """EmulatedChannel resource
+
+ This resources serves as a base class for wireless channels emulated by
+ means of ns3 simulation.
+
+ Attributes:
+ ap (Reference[node]): Reference to the AP node
+ stations (Reference[node]): Reference to the list of stations.
+ control_port (int): Port used to communicate with the management
+ interface of the simulation.
+
+ Implementation notes:
+ - Both AP and stations are allocated a separate VLAN to isolate broadcast
+ traffic and prevent loops on the bridge.
+ - We also need that all interfaces related to ap and stations are created
+ before we run the commandline (currently, dynamically adding/removing
+ AP and stations is not supported by the emulator). This is made
+ possible thanks to the key=True parameter, which makes sure the
+ attributes are processed before the __create__ is called.
+
+ Todo:
+ - Retrieve the process PID to kill it during __delete__
+ """
+
+ __resource_type__ = BaseResource
+
+ ap = Attribute(Node, description = 'AP', key = True)
+ stations = Attribute(Node, description = 'List of stations',
+ multiplicity = Multiplicity.OneToMany, key = True)
+ control_port = Attribute(Integer,
+ description = 'Control port for the simulation')
+
+ # Overloaded attributes
+ node = Attribute(requirements = [
+ Requirement('bridge')
+ ])
+
+ #--------------------------------------------------------------------------
+ # Constructor
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ # AP (resp. stations) interfaces (for external connectivity) and
+ # tap_devices (for connection to emulator)
+ self._ap_if = None
+ self._ap_tap = None
+ self._sta_ifs = dict()
+ self._sta_taps = dict()
+
+ # Device names to be attached to the bridge (differs according to the
+ # node type, Physical or LxcContainer, and eventually None for an
+ # unmanaged stations)
+ self._ap_bridged = None
+ self._sta_bridged = dict()
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ @inline_task
+ def __get__(self):
+ raise ResourceNotFound
+
+ def __create__(self):
+ # NOTE: http://stackoverflow.com/questions/21141352/python-subprocess-
+ # calling-a-script-which-runs-a-background-process-hanging
+ # The output of the background scripts is still going to the same file
+ # descriptor as the child script, thus the parent script waits for it
+ # to finish.
+ cmd = '(' + self.__app_name__ + ' ' + self._get_cmdline_params() + \
+ '>/dev/null 2>&1) &'
+ return BashTask(self.node, cmd)
+
+ def __delete__(self):
+ raise NotImplementedError
+
+ #--------------------------------------------------------------------------
+ # Attribute handlers
+ #--------------------------------------------------------------------------
+
+ @async_task
+ async def _set_ap(self, ap=None):
+ if ap is None:
+ ap = self.ap
+ if ap is None:
+ log.info('Ignored setting ap to None...')
+ return
+
+ # Add a WiFi interface for the AP...
+ interfaces = list()
+ if isinstance(ap, LxcContainer):
+ # Ideally, We need to create a VethPair for each station
+ # This should be monitored for the total channel bw
+ host = NetDevice(node = ap.node,
+ device_name='vhh-' + ap.name + '-' + self.name,
+ monitored = False,
+ managed = False)
+ self._ap_if = VethPair(node = self.ap,
+ name = 'vh-' + ap.name + '-' + self.name,
+ device_name = 'vh-' + ap.name + '-' + self.name,
+ host = host,
+ owner = self)
+ self._ap_bridged = self._ap_if.host
+ else:
+ raise NotImplementedError
+ self._state.manager.commit_resource(self._ap_if)
+
+ interfaces.append(self._ap_if)
+
+ # Add a tap interface for the AP...
+ self._ap_tap = TapDevice(node = self.node,
+ owner = self,
+ device_name = 'tap-' + ap.name + '-' + self.name,
+ up = True,
+ promiscuous = True,
+ monitored = False)
+ self._state.manager.commit_resource(self._ap_tap)
+ interfaces.append(self._ap_tap)
+
+ # Wait for interfaces to be setup
+ await wait_resources(interfaces)
+
+ # NOTE: only set channel after the resource is created or it might
+ # create loops which, at this time, are not handled
+ self._ap_if.set('channel', self)
+
+ # Add interfaces to bridge
+ vlan = AddressManager().get('vlan', self, tag='ap')
+
+ # AS the container has created the VethPair already without Vlan, we
+ # need to delete and recreate it
+ task = self.node.bridge._remove_interface(self._ap_bridged)
+ await run_task(task, self._state.manager)
+ task = self.node.bridge._add_interface(self._ap_bridged, vlan = vlan)
+ await run_task(task, self._state.manager)
+
+ task = self.node.bridge._add_interface(self._ap_tap, vlan = vlan)
+ await run_task(task, self._state.manager)
+
+ print('/!\ pass information to the running simulation')
+
+ @inline_task
+ def _get_ap(self):
+ return {'ap': None}
+
+ @inline_task
+ def _get_stations(self):
+ return {'stations': list()}
+
+ @async_task
+ async def _set_stations(self, stations=None):
+ print('adding stations...')
+ if stations is None:
+ stations = self.stations
+
+ for station in stations:
+ await self._add_station(station)
+
+ def _add_stations(self, stations):
+ raise NotImplementedError
+
+ @inline_task
+ def _remove_stations(self, station):
+ raise NotImplementedError
+