Adding environment setting features for applications under OMF
[nepi.git] / src / nepi / testbeds / omf / metadata.py
1 # -*- coding: utf-8 -*-
2
3 import functools
4 import weakref
5
6 from constants import TESTBED_ID, TESTBED_VERSION
7 from nepi.core import metadata
8 from nepi.core.attributes import Attribute
9 from nepi.util import tags, validation
10 from nepi.util.constants import ApplicationStatus as AS, \
11         FactoryCategories as FC, DeploymentConfiguration as DC
12
13 ##############################################################################
14
15 class OmfResource(object):
16     def __init__(self, guid, tc):
17         super(OmfResource, self).__init__()
18         self._tc = weakref.ref(tc)
19         self._guid = guid
20
21     @property
22     def tc(self):
23         return self._tc and self._tc()
24
25     def configure(self):
26         pass
27
28     def start(self):
29         pass
30
31     def stop(self):
32         pass
33
34     def status(self):
35         pass
36
37     def shutdown(self):
38         pass
39
40 ## NODE #######################################################################
41
42 class OmfNode(OmfResource):
43     def __init__(self, guid, tc):
44         super(OmfNode, self).__init__(guid, tc)
45         self.hostname = self.tc._get_parameters(guid)['hostname']
46         self.tc.api.enroll_host(self.hostname)
47
48 ## APPLICATION ################################################################
49
50 class OmfApplication(OmfResource):
51     def __init__(self, guid, tc):
52         super(OmfApplication, self).__init__(guid, tc)
53         node_guids = tc.get_connected(guid, "node", "apps")
54         if len(node_guids) == 0:
55             raise RuntimeError("Can't instantiate interface %d outside node" % guid)
56
57         self._node_guid = node_guids[0] 
58         self.app_id = None
59         self.arguments = None
60         self.path = None
61         self.env = None
62
63     def start(self):
64         node = self.tc.elements.get(self._node_guid)
65         self.tc.api.execute(node.hostname, 
66                 self.appId, 
67                 self.arguments, 
68                 self.path,
69                 self.env)
70
71     def stop(self):
72         node = self.tc.elements.get(self._node_guid)
73         self.tc.api.exit(node.hostname, 
74                 self.appId) 
75
76     def status(self):
77         if guid not in testbed_instance.elements.keys():
78             return AS.STATUS_NOT_STARTED
79         return AS.STATUS_RUNNING
80         # TODO!!!!
81         #return AS.STATUS_FINISHED
82
83
84 ## WIFIIFACE ########################################################
85
86 class OmfWifiInterface(OmfResource):
87     def __init__(self, guid, tc):
88         super(OmfWifiInterface, self).__init__(guid, tc)
89         node_guids = tc.get_connected(guid, "node", "devs")
90         if len(node_guids) == 0:
91             raise RuntimeError("Can't instantiate interface %d outside node" % guid)
92
93         self._node_guid = node_guids[0] 
94         self.mode = None
95         self.type = None
96         self.essid = None
97         self.channel = None
98         self.ip = None
99
100     def __setattr__(self, name, value):
101         if name in ["ip", "mode", "type", "essid", "channel"]:
102             node = self.tc.elements.get(self._node_guid)    
103             attribute = "net/w0/%s" % name
104             self._tc().api.configure(node.hostname, attribute, value)
105         else:
106             super(OmfWifiInterface, self).__setattr__(name, value)
107
108 # Factories
109 NODE = "Node"
110 WIFIIFACE = "WifiInterface"
111 CHANNEL = "Channel"
112 OMFAPPLICATION = "OmfApplication"
113
114 def create(factory, testbed_instance, guid):
115     clazz = OmfResource
116     if factory == NODE:
117         clazz = OmfNode
118     elif factory == OMFAPPLICATION:
119         clazz = OmfApplication
120     elif factory == WIFIIFACE:
121         clazz = OmfWifiInterface
122
123     element = clazz(guid, testbed_instance)
124     #import pdb; pdb.set_trace()
125     testbed_instance._elements[guid] = element
126
127 def start(testbed_instance, guid):
128     element = testbed_instance.elements.get(guid)
129     element.start()
130
131 def stop(testbed_instance, guid):
132     element = testbed_instance.elements.get(guid)
133     element.stop()
134
135 def status(testbed_instance, guid):
136     element = testbed_instance.elements.get(guid)
137     return element.status()
138
139 def configure(testbed_instance, guid):
140     element = testbed_instance.elements.get(guid)
141     return element.status()
142
143 ### Factory information ###
144
145 connector_types = dict({
146     "apps": dict({
147                 "help": "Connector from node to applications", 
148                 "name": "apps",
149                 "max": -1, 
150                 "min": 0
151             }),
152     "devs": dict({
153                 "help": "Connector to network interfaces", 
154                 "name": "devs",
155                 "max": -1, 
156                 "min": 0
157             }),
158     "chan": dict({
159                 "help": "Connector from a device to a channel", 
160                 "name": "chan",
161                 "max": 1, 
162                 "min": 1
163             }),
164     "node": dict({
165                 "help": "Connector to a Node", 
166                 "name": "node",
167                 "max": 1, 
168                 "min": 1
169             }),
170    })
171
172 connections = [
173     dict({
174         "from": (TESTBED_ID, NODE, "devs"),
175         "to":   (TESTBED_ID, WIFIIFACE, "node"),
176         "can_cross": False
177     }),
178     dict({
179         "from": (TESTBED_ID, WIFIIFACE, "chan"),
180         "to":   (TESTBED_ID, CHANNEL, "devs"),
181         "can_cross": False
182     }),
183     dict({
184         "from": (TESTBED_ID, NODE, "apps"),
185         "to":   (TESTBED_ID, OMFAPPLICATION, "node"),
186         "can_cross": False
187     }),
188  ]
189
190 attributes = dict({
191     "appId": dict({
192                 "name": "appId",
193                 "help": "Application id",
194                 "type": Attribute.STRING,
195                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
196                 "validation_function": validation.is_string
197             }),
198     "arguments": dict({
199                 "name": "arguments",
200                 "help": "Application arguments",
201                 "type": Attribute.STRING,
202                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
203                 "validation_function": validation.is_string
204             }),
205     "path": dict({
206                 "name": "path",
207                 "help": "Path to binary (e.g '/opt/vlc-1.1.13/vlc')",
208                 "type": Attribute.STRING,
209                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
210                 "validation_function": validation.is_string
211             }),
212     "env": dict({
213                 "name": "env",
214                 "help": "String with space separated values of environment variables to set before starting application (e.g 'FOO=foo BAR=bar')",
215                 "type": Attribute.STRING,
216                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
217                 "validation_function": validation.is_string
218             }),
219     "hostname": dict({
220                 "name": "hostname",
221                 "help": "Hostname for the target OMF node",
222                 "type": Attribute.STRING,
223                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
224                 "validation_function": validation.is_string
225             }),
226     "mode": dict({
227                 "name": "mode",
228                 "help": "Corresponds to the OMF attributes net/w0/mode",
229                 "type": Attribute.STRING,
230                 "flags": Attribute.NoDefaultValue, 
231                 "validation_function": validation.is_string
232             }),
233     "type": dict({
234                 "name": "type",
235                 "help": "Corresponds to the OMF attributes net/w0/type",
236                 "type": Attribute.STRING,
237                 "flags": Attribute.NoDefaultValue, 
238                 "validation_function": validation.is_string
239             }),
240     "channel": dict({
241                 "name": "channel",
242                 "help": "Corresponds to the OMF attributes net/w0/channel",
243                 "type": Attribute.STRING,
244                 "flags": Attribute.NoDefaultValue, 
245                 "validation_function": validation.is_string
246             }),
247     "essid": dict({
248                 "name": "essid",
249                 "help": "Corresponds to the OMF attributes net/w0/essid",
250                 "type": Attribute.STRING,
251                 "flags": Attribute.NoDefaultValue, 
252                 "validation_function": validation.is_string
253             }),
254     "ip": dict({
255                 "name": "ip",
256                 "help": "Corresponds to the OMF attributes net/w0/ip",
257                 "type": Attribute.STRING,
258                 "flags": Attribute.NoDefaultValue, 
259                 "validation_function": validation.is_ip4_address
260             }),
261
262
263
264     })
265
266 traces = dict()
267
268 create_order = [ NODE, WIFIIFACE, CHANNEL, OMFAPPLICATION ]
269 configure_order = [ WIFIIFACE,  NODE, CHANNEL, OMFAPPLICATION ]
270
271 factories_info = dict({
272     NODE: dict({
273             "help": "OMF Node",
274             "category": FC.CATEGORY_NODES,
275             "create_function": functools.partial(create, NODE),
276             "box_attributes": ["hostname"],
277             "connector_types": ["devs", "apps"],
278             "tags": [tags.NODE, tags.ALLOW_ROUTES],
279        }),
280     WIFIIFACE: dict({
281             "help": "Wireless network interface",
282             "category": FC.CATEGORY_DEVICES,
283             "create_function": functools.partial(create, WIFIIFACE),
284             "configure_function": configure,
285             "box_attributes": ["mode", "type", "channel", "essid", "ip"],
286             "connector_types": ["node", "chan"],
287             "tags": [tags.INTERFACE, tags.HAS_ADDRESSES],
288        }),
289     CHANNEL: dict({
290             "help": "Wireless channel",
291             "category": FC.CATEGORY_DEVICES,
292             "create_function": create,
293             "create_function": functools.partial(create, CHANNEL),
294             "box_attributes": ["mode", "type", "channel", "essid"],
295             "connector_types": ["devs"],
296        }),
297     OMFAPPLICATION: dict({
298             "help": "Generic executable command line application",
299             "category": FC.CATEGORY_APPLICATIONS,
300             "create_function": functools.partial(create, OMFAPPLICATION),
301             "start_function": start,
302             "stop_function": stop,
303             "status_function": status,
304             "box_attributes": ["appId", "arguments", "path", "env"],
305             "connector_types": ["node"],
306             "tags": [tags.APPLICATION],
307         }),
308 })
309
310 testbed_attributes = dict({
311     "enable_debug": dict({
312             "name": "enableDebug",
313             "help": "Enable netns debug output",
314             "type": Attribute.BOOL,
315             "value": False,
316             "validation_function": validation.is_bool
317         }),
318     "xmppSlice": dict({
319                 "name": "xmppSlice",
320                 "help": "OMF slice",
321                 "type": Attribute.STRING,
322                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
323                 "validation_function": validation.is_string
324             }),
325     "xmppHost": dict({
326                 "name": "xmppHost",
327                 "help": "OMF XMPP server host",
328                 "type": Attribute.STRING,
329                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
330                 "validation_function": validation.is_string
331             }),
332     "xmppPort": dict({
333                 "name": "xmppPort",
334                 "help": "OMF XMPP service port",
335                 "type": Attribute.INTEGER,
336                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
337                 "validation_function": validation.is_integer
338             }),
339     "xmppPassword": dict({
340                 "name": "xmppPassword",
341                 "help": "OMF XMPP slice password",
342                 "type": Attribute.STRING,
343                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
344                 "validation_function": validation.is_string
345             }),
346     })
347
348 supported_recovery_policies = [
349         DC.POLICY_FAIL,
350     ]
351
352 class MetadataInfo(metadata.MetadataInfo):
353     @property
354     def connector_types(self):
355         return connector_types
356
357     @property
358     def connections(self):
359         return connections
360
361     @property
362     def attributes(self):
363         return attributes
364
365     @property
366     def traces(self):
367         return traces
368
369     @property
370     def create_order(self):
371         return create_order
372
373     @property
374     def configure_order(self):
375         return configure_order
376
377     @property
378     def factories_info(self):
379         return factories_info
380
381     @property
382     def testbed_attributes(self):
383         return testbed_attributes
384
385     @property
386     def testbed_id(self):
387         return TESTBED_ID
388
389     @property
390     def testbed_version(self):
391         return TESTBED_VERSION
392     
393     @property
394     def supported_recover_policies(self):
395         return supported_recovery_policies
396