ia64/xen-unstable

view tools/python/xen/xend/server/netif.py @ 3010:0028a5b33f67

bitkeeper revision 1.1159.170.36 (419b409dgOm9WpCUrkZWaLcFuKDTIw)

Solve hang at net frontend initialization by necessarily passing domain ID to NetDev in xend.
This follows Andy's cset to support backends in unprivileged domains.
author bren@anvil.research
date Wed Nov 17 12:14:21 2004 +0000 (2004-11-17)
parents 6a8849c4b647
children 51b019333cfa 687594c02d83
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
107 self.dom = ctrl.dom
109 def _get_config_mac(self, config):
110 vmac = sxp.child_value(config, 'mac')
111 if not vmac: return None
112 mac = [ int(x, 16) for x in vmac.split(':') ]
113 if len(mac) != 6: raise XendError("invalid mac")
114 return mac
116 def _get_config_ipaddr(self, config):
117 ips = sxp.children(config, elt='ip')
118 if ips:
119 val = []
120 for ipaddr in ips:
121 val.append(sxp.child0(ipaddr))
122 else:
123 val = None
124 return val
126 def configure(self, config, change=0):
127 if change:
128 return self.reconfigure(config)
129 self.config = config
130 self.mac = None
131 self.bridge = None
132 self.script = None
133 self.ipaddr = []
135 mac = self._get_config_mac(config)
136 if mac is None:
137 raise XendError("invalid mac")
138 self.mac = mac
139 self.bridge = sxp.child_value(config, 'bridge')
140 self.script = sxp.child_value(config, 'script')
141 self.ipaddr = self._get_config_ipaddr(config) or []
143 try:
144 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
145 except:
146 raise XendError('invalid backend domain')
148 def reconfigure(self, config):
149 """Reconfigure the interface with new values.
150 Not all configuration parameters can be changed:
151 bridge, script and ip addresses can,
152 backend and mac cannot.
154 To leave a parameter unchanged, omit it from the changes.
156 @param config configuration changes
157 @return updated interface configuration
158 @raise XendError on errors
159 """
160 changes = {}
161 mac = self._get_config_mac(config)
162 bridge = sxp.child_value(config, 'bridge')
163 script = sxp.child_value(config, 'script')
164 ipaddr = self._get_config_ipaddr(config)
165 backendDomain = sxp.child_value(config, 'backend', '0')
166 if (mac is not None) and (mac != self.mac):
167 raise XendError("cannot change mac")
168 if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
169 raise XendError("cannot change backend")
170 if (bridge is not None) and (bridge != self.bridge):
171 changes['bridge'] = bridge
172 if (script is not None) and (script != self.script):
173 changes['script'] = script
174 if (ipaddr is not None) and (ipaddr != self.ipaddr):
175 changes['ipaddr'] = ipaddr
177 if changes:
178 self.vifctl("down")
179 for (k, v) in changes.items():
180 setattr(self, k, v)
181 self.config = sxp.merge(config, self.config)
182 self.vifctl("up")
183 return self.config
185 def sxpr(self):
186 vif = str(self.vif)
187 mac = self.get_mac()
188 val = ['vif',
189 ['idx', self.idx],
190 ['vif', vif],
191 ['mac', mac]]
192 if self.bridge:
193 val.append(['bridge', self.bridge])
194 if self.script:
195 val.append(['script', self.script])
196 for ip in self.ipaddr:
197 val.append(['ip', ip])
198 if self.evtchn:
199 val.append(['evtchn',
200 self.evtchn['port1'],
201 self.evtchn['port2']])
202 if self.index is not None:
203 val.append(['index', self.index])
204 return val
206 def get_vifname(self):
207 """Get the virtual interface device name.
208 """
209 return "vif%d.%d" % (self.controller.dom, self.vif)
211 def get_mac(self):
212 """Get the MAC address as a string.
213 """
214 return ':'.join(map(lambda x: "%02x" % x, self.mac))
216 def vifctl_params(self, vmname=None):
217 """Get the parameters to pass to vifctl.
218 """
219 dom = self.controller.dom
220 if vmname is None:
221 xd = get_component('xen.xend.XendDomain')
222 try:
223 vm = xd.domain_lookup(dom)
224 vmname = vm.name
225 except:
226 vmname = 'DOM%d' % dom
227 return { 'domain': vmname,
228 'vif' : self.get_vifname(),
229 'mac' : self.get_mac(),
230 'bridge': self.bridge,
231 'script': self.script,
232 'ipaddr': self.ipaddr, }
234 def vifctl(self, op, vmname=None):
235 """Bring the device up or down.
236 The vmname is needed when bringing a device up for a new domain because
237 the domain is not yet in the table so we can't look its name up.
239 @param op: operation name (up, down)
240 @param vmname: vmname
241 """
242 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
243 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
244 if vnet:
245 vnet.vifctl(op, self.get_vifname(), self.get_mac())
247 def attach(self):
248 d = self.send_be_create()
249 d.addCallback(self.respond_be_create)
250 return d
252 def getEventChannelBackend(self):
253 val = 0
254 if self.evtchn:
255 val = self.evtchn['port1']
256 return val
258 def getEventChannelFrontend(self):
259 val = 0
260 if self.evtchn:
261 val = self.evtchn['port2']
262 return val
264 def send_be_create(self):
265 d = defer.Deferred()
266 msg = packMsg('netif_be_create_t',
267 { 'domid' : self.controller.dom,
268 'netif_handle' : self.vif,
269 'mac' : self.mac })
270 self.getBackendInterface().writeRequest(msg, response=d)
271 return d
273 def respond_be_create(self, msg):
274 val = unpackMsg('netif_be_create_t', msg)
275 return self
277 def destroy(self, change=0):
278 """Destroy the device's resources and disconnect from the back-end
279 device controller. If 'change' is true notify the front-end interface.
281 @param change: change flag
282 """
283 self.status = NETIF_INTERFACE_STATUS_CLOSED
284 def cb_destroy(val):
285 self.send_be_destroy()
286 self.getBackendInterface().close()
287 if change:
288 self.reportStatus()
289 log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
290 self.vifctl('down')
291 d = self.send_be_disconnect()
292 d.addCallback(cb_destroy)
294 def send_be_disconnect(self):
295 d = defer.Deferred()
296 msg = packMsg('netif_be_disconnect_t',
297 { 'domid' : self.controller.dom,
298 'netif_handle' : self.vif })
299 self.getBackendInterface().writeRequest(msg, response=d)
300 return d
302 def send_be_destroy(self, response=None):
303 msg = packMsg('netif_be_destroy_t',
304 { 'domid' : self.controller.dom,
305 'netif_handle' : self.vif })
306 self.controller.delDevice(self.vif)
307 self.getBackendInterface().writeRequest(msg, response=response)
309 def recv_fe_interface_connect(self, val, req):
310 if not req: return
311 self.evtchn = channel.eventChannel(self.dom, self.controller.dom)
312 msg = packMsg('netif_be_connect_t',
313 { 'domid' : self.controller.dom,
314 'netif_handle' : self.vif,
315 'evtchn' : self.getEventChannelBackend(),
316 'tx_shmem_frame' : val['tx_shmem_frame'],
317 'rx_shmem_frame' : val['rx_shmem_frame'] })
318 d = defer.Deferred()
319 d.addCallback(self.respond_be_connect)
320 self.getBackendInterface().writeRequest(msg, response=d)
322 def respond_be_connect(self, msg):
323 val = unpackMsg('netif_be_connect_t', msg)
324 dom = val['domid']
325 vif = val['netif_handle']
326 self.status = NETIF_INTERFACE_STATUS_CONNECTED
327 self.reportStatus()
329 def reportStatus(self, resp=0):
330 msg = packMsg('netif_fe_interface_status_t',
331 { 'handle' : self.vif,
332 'status' : self.status,
333 'evtchn' : self.getEventChannelFrontend(),
334 'domid' : self.backendDomain,
335 'mac' : self.mac })
336 if resp:
337 self.controller.writeResponse(msg)
338 else:
339 self.controller.writeRequest(msg)
341 def interfaceChanged(self):
342 """Notify the font-end that a device has been added or removed.
343 """
344 self.reportStatus()
346 class NetifController(controller.SplitController):
347 """Network interface controller. Handles all network devices for a domain.
348 """
350 def __init__(self, factory, dom):
351 controller.SplitController.__init__(self, factory, dom)
352 self.devices = {}
353 self.addMethod(CMSG_NETIF_FE,
354 CMSG_NETIF_FE_DRIVER_STATUS,
355 self.recv_fe_driver_status)
356 self.addMethod(CMSG_NETIF_FE,
357 CMSG_NETIF_FE_INTERFACE_STATUS,
358 self.recv_fe_interface_status)
359 self.addMethod(CMSG_NETIF_FE,
360 CMSG_NETIF_FE_INTERFACE_CONNECT,
361 self.recv_fe_interface_connect)
362 self.registerChannel()
364 def sxpr(self):
365 val = ['netif', ['dom', self.dom]]
366 return val
368 def lostChannel(self):
369 """Method called when the channel has been lost.
370 """
371 controller.Controller.lostChannel(self)
373 def addDevice(self, vif, config):
374 """Add a network interface.
376 @param vif: device index
377 @param config: device configuration
378 @return: device
379 """
380 if vif in self.devices:
381 raise XendError('device exists:' + str(vif))
382 dev = NetDev(vif, self, config)
383 self.devices[vif] = dev
384 return dev
386 def destroy(self):
387 """Destroy the controller and all devices.
388 """
389 self.destroyDevices()
391 def destroyDevices(self):
392 """Destroy all devices.
393 """
394 for dev in self.getDevices():
395 dev.destroy()
397 def attachDevice(self, vif, config, recreate=0):
398 """Attach a network device.
400 @param vif: interface index
401 @param config: device configuration
402 @param recreate: recreate flag (true after xend restart)
403 @return: deferred
404 """
405 dev = self.addDevice(vif, config)
406 if recreate:
407 d = defer.succeed(dev)
408 else:
409 d = dev.attach()
410 return d
412 def recv_fe_driver_status(self, msg, req):
413 if not req: return
414 print
415 print 'recv_fe_driver_status>'
416 msg = packMsg('netif_fe_driver_status_t',
417 { 'status' : NETIF_DRIVER_STATUS_UP,
418 ## FIXME: max_handle should be max active interface id
419 'max_handle' : len(self.devices)
420 #'max_handle' : self.getMaxDeviceIdx()
421 })
422 # Two ways of doing it:
423 # 1) front-end requests driver status, we reply with the interface count,
424 # front-end polls the interfaces,
425 # front-end checks they are all up
426 # 2) front-end requests driver status, we reply (with anything),
427 # we notify the interfaces,
428 # we notify driver status up with the count
429 # front-end checks they are all up
430 #
431 # We really want to use 1), but at the moment the xenU kernel panics
432 # in that mode, so we're sticking to 2) for now.
433 resp = 0
434 if resp:
435 self.writeResponse(msg)
436 else:
437 for dev in self.devices.values():
438 dev.reportStatus()
439 self.writeRequest(msg)
440 return resp
442 def recv_fe_interface_status(self, msg, req):
443 if not req: return
444 print
445 val = unpackMsg('netif_fe_interface_status_t', msg)
446 print "recv_fe_interface_status>", val
447 vif = val['handle']
448 dev = self.findDevice(vif)
449 if dev:
450 print 'recv_fe_interface_status>', 'dev=', dev
451 dev.reportStatus(resp=1)
452 else:
453 msg = packMsg('netif_fe_interface_status_t',
454 { 'handle' : -1,
455 'status' : NETIF_INTERFACE_STATUS_CLOSED,
456 });
457 print 'recv_fe_interface_status>', 'no dev, returning -1'
458 self.writeResponse(msg)
459 return 1
462 def recv_fe_interface_connect(self, msg, req):
463 val = unpackMsg('netif_fe_interface_connect_t', msg)
464 vif = val['handle']
465 print
466 print "recv_fe_interface_connect", val
467 dev = self.getDevice(vif)
468 if dev:
469 dev.recv_fe_interface_connect(val, req)
470 else:
471 log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
472 self.dom, vif)