ia64/xen-unstable

view tools/python/xen/xend/server/netif.py @ 3022:401d110b812b

bitkeeper revision 1.1159.170.43 (419b79810ziXBmhNHCQ-Z6wgYwAp7w)

Merge br260@labyrinth.cl.cam.ac.uk:/usr/groups/xeno/BK/xeno.bk
into anvil.research:/scratch/anvil/bren/xeno.bk
author bren@anvil.research
date Wed Nov 17 16:17:05 2004 +0000 (2004-11-17)
parents 51b019333cfa 687594c02d83
children f5a6b3a8c9b9 77732aef762e 0dc3b8b8c298
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual network interfaces.
3 """
5 import random
7 from twisted.internet import defer
9 from xen.xend import sxp
10 from xen.xend import Vifctl
11 from xen.xend.XendError import XendError
12 from xen.xend.XendLogging import log
13 from xen.xend import XendVnet
14 from xen.xend.XendRoot import get_component
16 import channel
17 import controller
18 from messages import *
20 class NetifBackendController(controller.BackendController):
21 """Handler for the 'back-end' channel to a network device driver domain.
22 """
24 def __init__(self, ctrl, dom):
25 controller.BackendController.__init__(self, ctrl, dom)
26 self.addMethod(CMSG_NETIF_BE,
27 CMSG_NETIF_BE_DRIVER_STATUS,
28 self.recv_be_driver_status)
29 self.registerChannel()
31 def recv_be_driver_status(self, msg, req):
32 val = unpackMsg('netif_be_driver_status_t', msg)
33 status = val['status']
35 class NetifBackendInterface(controller.BackendInterface):
36 """Handler for the 'back-end' channel to a network device driver domain
37 on behalf of a front-end domain.
39 Each network device is handled separately, so we add no functionality
40 here.
41 """
43 pass
45 class NetifControllerFactory(controller.SplitControllerFactory):
46 """Factory for creating network interface controllers.
47 """
49 def __init__(self):
50 controller.SplitControllerFactory.__init__(self)
52 def createController(self, dom):
53 """Create a network interface controller for a domain.
55 @param dom: domain
56 @return: netif controller
57 """
58 return NetifController(self, dom)
60 def createBackendController(self, dom):
61 """Create a network device backend controller.
63 @param dom: backend domain
64 @return: backend controller
65 """
66 return NetifBackendController(self, dom)
68 def createBackendInterface(self, ctrl, dom, handle):
69 """Create a network device backend interface.
71 @param ctrl: controller
72 @param dom: backend domain
73 @param handle: interface handle
74 @return: backend interface
75 """
76 return NetifBackendInterface(ctrl, dom, handle)
78 def getDomainDevices(self, dom):
79 """Get the network devices for a domain.
81 @param dom: domain
82 @return: netif controller list
83 """
84 netif = self.getControllerByDom(dom)
85 return (netif and netif.getDevices()) or []
87 def getDomainDevice(self, dom, vif):
88 """Get a virtual network interface device for a domain.
90 @param dom: domain
91 @param vif: virtual interface index
92 @return: NetDev
93 """
94 netif = self.getControllerByDom(dom)
95 return (netif and netif.getDevice(vif)) or None
97 class NetDev(controller.SplitDev):
98 """Info record for a network device.
99 """
101 def __init__(self, vif, ctrl, config):
102 controller.SplitDev.__init__(self, vif, ctrl)
103 self.vif = vif
104 self.evtchn = None
105 self.configure(config)
106 self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
108 def _get_config_mac(self, config):
109 vmac = sxp.child_value(config, 'mac')
110 if not vmac: return None
111 mac = [ int(x, 16) for x in vmac.split(':') ]
112 if len(mac) != 6: raise XendError("invalid mac")
113 return mac
115 def _get_config_ipaddr(self, config):
116 ips = sxp.children(config, elt='ip')
117 if ips:
118 val = []
119 for ipaddr in ips:
120 val.append(sxp.child0(ipaddr))
121 else:
122 val = None
123 return val
125 def configure(self, config, change=0):
126 if change:
127 return self.reconfigure(config)
128 self.config = config
129 self.mac = None
130 self.bridge = None
131 self.script = None
132 self.ipaddr = []
134 mac = self._get_config_mac(config)
135 if mac is None:
136 raise XendError("invalid mac")
137 self.mac = mac
138 self.bridge = sxp.child_value(config, 'bridge')
139 self.script = sxp.child_value(config, 'script')
140 self.ipaddr = self._get_config_ipaddr(config) or []
142 try:
143 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
144 except:
145 raise XendError('invalid backend domain')
147 def reconfigure(self, config):
148 """Reconfigure the interface with new values.
149 Not all configuration parameters can be changed:
150 bridge, script and ip addresses can,
151 backend and mac cannot.
153 To leave a parameter unchanged, omit it from the changes.
155 @param config configuration changes
156 @return updated interface configuration
157 @raise XendError on errors
158 """
159 changes = {}
160 mac = self._get_config_mac(config)
161 bridge = sxp.child_value(config, 'bridge')
162 script = sxp.child_value(config, 'script')
163 ipaddr = self._get_config_ipaddr(config)
164 backendDomain = sxp.child_value(config, 'backend', '0')
165 if (mac is not None) and (mac != self.mac):
166 raise XendError("cannot change mac")
167 if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
168 raise XendError("cannot change backend")
169 if (bridge is not None) and (bridge != self.bridge):
170 changes['bridge'] = bridge
171 if (script is not None) and (script != self.script):
172 changes['script'] = script
173 if (ipaddr is not None) and (ipaddr != self.ipaddr):
174 changes['ipaddr'] = ipaddr
176 if changes:
177 self.vifctl("down")
178 for (k, v) in changes.items():
179 setattr(self, k, v)
180 self.config = sxp.merge(config, self.config)
181 self.vifctl("up")
182 return self.config
184 def sxpr(self):
185 vif = str(self.vif)
186 mac = self.get_mac()
187 val = ['vif',
188 ['idx', self.idx],
189 ['vif', vif],
190 ['mac', mac]]
191 if self.bridge:
192 val.append(['bridge', self.bridge])
193 if self.script:
194 val.append(['script', self.script])
195 for ip in self.ipaddr:
196 val.append(['ip', ip])
197 if self.evtchn:
198 val.append(['evtchn',
199 self.evtchn['port1'],
200 self.evtchn['port2']])
201 if self.index is not None:
202 val.append(['index', self.index])
203 return val
205 def get_vifname(self):
206 """Get the virtual interface device name.
207 """
208 return "vif%d.%d" % (self.controller.dom, self.vif)
210 def get_mac(self):
211 """Get the MAC address as a string.
212 """
213 return ':'.join(map(lambda x: "%02x" % x, self.mac))
215 def vifctl_params(self, vmname=None):
216 """Get the parameters to pass to vifctl.
217 """
218 dom = self.controller.dom
219 if vmname is None:
220 xd = get_component('xen.xend.XendDomain')
221 try:
222 vm = xd.domain_lookup(dom)
223 vmname = vm.name
224 except:
225 vmname = 'DOM%d' % dom
226 return { 'domain': vmname,
227 'vif' : self.get_vifname(),
228 'mac' : self.get_mac(),
229 'bridge': self.bridge,
230 'script': self.script,
231 'ipaddr': self.ipaddr, }
233 def vifctl(self, op, vmname=None):
234 """Bring the device up or down.
235 The vmname is needed when bringing a device up for a new domain because
236 the domain is not yet in the table so we can't look its name up.
238 @param op: operation name (up, down)
239 @param vmname: vmname
240 """
241 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
242 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
243 if vnet:
244 vnet.vifctl(op, self.get_vifname(), self.get_mac())
246 def attach(self):
247 d = self.send_be_create()
248 d.addCallback(self.respond_be_create)
249 return d
251 def getEventChannelBackend(self):
252 val = 0
253 if self.evtchn:
254 val = self.evtchn['port1']
255 return val
257 def getEventChannelFrontend(self):
258 val = 0
259 if self.evtchn:
260 val = self.evtchn['port2']
261 return val
263 def send_be_create(self):
264 d = defer.Deferred()
265 msg = packMsg('netif_be_create_t',
266 { 'domid' : self.controller.dom,
267 'netif_handle' : self.vif,
268 'mac' : self.mac })
269 self.getBackendInterface().writeRequest(msg, response=d)
270 return d
272 def respond_be_create(self, msg):
273 val = unpackMsg('netif_be_create_t', msg)
274 return self
276 def destroy(self, change=0):
277 """Destroy the device's resources and disconnect from the back-end
278 device controller. If 'change' is true notify the front-end interface.
280 @param change: change flag
281 """
282 self.status = NETIF_INTERFACE_STATUS_CLOSED
283 def cb_destroy(val):
284 self.send_be_destroy()
285 self.getBackendInterface().close()
286 if change:
287 self.reportStatus()
288 log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
289 self.vifctl('down')
290 d = self.send_be_disconnect()
291 d.addCallback(cb_destroy)
293 def send_be_disconnect(self):
294 d = defer.Deferred()
295 msg = packMsg('netif_be_disconnect_t',
296 { 'domid' : self.controller.dom,
297 'netif_handle' : self.vif })
298 self.getBackendInterface().writeRequest(msg, response=d)
299 return d
301 def send_be_destroy(self, response=None):
302 msg = packMsg('netif_be_destroy_t',
303 { 'domid' : self.controller.dom,
304 'netif_handle' : self.vif })
305 self.controller.delDevice(self.vif)
306 self.getBackendInterface().writeRequest(msg, response=response)
308 def recv_fe_interface_connect(self, val, req):
309 if not req: return
310 self.evtchn = channel.eventChannel(self.backendDomain, self.controller.dom)
311 msg = packMsg('netif_be_connect_t',
312 { 'domid' : self.controller.dom,
313 'netif_handle' : self.vif,
314 'evtchn' : self.getEventChannelBackend(),
315 'tx_shmem_frame' : val['tx_shmem_frame'],
316 'rx_shmem_frame' : val['rx_shmem_frame'] })
317 d = defer.Deferred()
318 d.addCallback(self.respond_be_connect)
319 self.getBackendInterface().writeRequest(msg, response=d)
321 def respond_be_connect(self, msg):
322 val = unpackMsg('netif_be_connect_t', msg)
323 dom = val['domid']
324 vif = val['netif_handle']
325 self.status = NETIF_INTERFACE_STATUS_CONNECTED
326 self.reportStatus()
328 def reportStatus(self, resp=0):
329 msg = packMsg('netif_fe_interface_status_t',
330 { 'handle' : self.vif,
331 'status' : self.status,
332 'evtchn' : self.getEventChannelFrontend(),
333 'domid' : self.backendDomain,
334 'mac' : self.mac })
335 if resp:
336 self.controller.writeResponse(msg)
337 else:
338 self.controller.writeRequest(msg)
340 def interfaceChanged(self):
341 """Notify the font-end that a device has been added or removed.
342 """
343 self.reportStatus()
345 class NetifController(controller.SplitController):
346 """Network interface controller. Handles all network devices for a domain.
347 """
349 def __init__(self, factory, dom):
350 controller.SplitController.__init__(self, factory, dom)
351 self.devices = {}
352 self.addMethod(CMSG_NETIF_FE,
353 CMSG_NETIF_FE_DRIVER_STATUS,
354 self.recv_fe_driver_status)
355 self.addMethod(CMSG_NETIF_FE,
356 CMSG_NETIF_FE_INTERFACE_STATUS,
357 self.recv_fe_interface_status)
358 self.addMethod(CMSG_NETIF_FE,
359 CMSG_NETIF_FE_INTERFACE_CONNECT,
360 self.recv_fe_interface_connect)
361 self.registerChannel()
363 def sxpr(self):
364 val = ['netif', ['dom', self.dom]]
365 return val
367 def lostChannel(self):
368 """Method called when the channel has been lost.
369 """
370 controller.Controller.lostChannel(self)
372 def addDevice(self, vif, config):
373 """Add a network interface.
375 @param vif: device index
376 @param config: device configuration
377 @return: device
378 """
379 if vif in self.devices:
380 raise XendError('device exists:' + str(vif))
381 dev = NetDev(vif, self, config)
382 self.devices[vif] = dev
383 return dev
385 def destroy(self):
386 """Destroy the controller and all devices.
387 """
388 self.destroyDevices()
390 def destroyDevices(self):
391 """Destroy all devices.
392 """
393 for dev in self.getDevices():
394 dev.destroy()
396 def attachDevice(self, vif, config, recreate=0):
397 """Attach a network device.
399 @param vif: interface index
400 @param config: device configuration
401 @param recreate: recreate flag (true after xend restart)
402 @return: deferred
403 """
404 dev = self.addDevice(vif, config)
405 if recreate:
406 d = defer.succeed(dev)
407 else:
408 d = dev.attach()
409 return d
411 def recv_fe_driver_status(self, msg, req):
412 if not req: return
413 print
414 print 'recv_fe_driver_status>'
415 msg = packMsg('netif_fe_driver_status_t',
416 { 'status' : NETIF_DRIVER_STATUS_UP,
417 ## FIXME: max_handle should be max active interface id
418 'max_handle' : len(self.devices)
419 #'max_handle' : self.getMaxDeviceIdx()
420 })
421 # Two ways of doing it:
422 # 1) front-end requests driver status, we reply with the interface count,
423 # front-end polls the interfaces,
424 # front-end checks they are all up
425 # 2) front-end requests driver status, we reply (with anything),
426 # we notify the interfaces,
427 # we notify driver status up with the count
428 # front-end checks they are all up
429 #
430 # We really want to use 1), but at the moment the xenU kernel panics
431 # in that mode, so we're sticking to 2) for now.
432 resp = 0
433 if resp:
434 self.writeResponse(msg)
435 else:
436 for dev in self.devices.values():
437 dev.reportStatus()
438 self.writeRequest(msg)
439 return resp
441 def recv_fe_interface_status(self, msg, req):
442 if not req: return
443 print
444 val = unpackMsg('netif_fe_interface_status_t', msg)
445 print "recv_fe_interface_status>", val
446 vif = val['handle']
447 dev = self.findDevice(vif)
448 if dev:
449 print 'recv_fe_interface_status>', 'dev=', dev
450 dev.reportStatus(resp=1)
451 else:
452 msg = packMsg('netif_fe_interface_status_t',
453 { 'handle' : -1,
454 'status' : NETIF_INTERFACE_STATUS_CLOSED,
455 });
456 print 'recv_fe_interface_status>', 'no dev, returning -1'
457 self.writeResponse(msg)
458 return 1
461 def recv_fe_interface_connect(self, msg, req):
462 val = unpackMsg('netif_fe_interface_connect_t', msg)
463 vif = val['handle']
464 print
465 print "recv_fe_interface_connect", val
466 dev = self.getDevice(vif)
467 if dev:
468 dev.recv_fe_interface_connect(val, req)
469 else:
470 log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
471 self.dom, vif)