1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
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
|