ia64/xen-unstable

view tools/python/xen/xend/server/netif.py @ 4635:b6ad195cf599

bitkeeper revision 1.1327.2.5 (426921fa25ZaWjkBe-mPZImv-iXvmg)

Message unpacking fix.
Signed-off-by: Mike Wray <mike.wray@hp.com>
author mjw@wray-m-3.hpl.hp.com
date Fri Apr 22 16:10:34 2005 +0000 (2005-04-22)
parents a838a908e38e
children b720e2fb6b00
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual network interfaces.
3 """
5 import random
7 from xen.xend import sxp
8 from xen.xend import Vifctl
9 from xen.xend.XendError import XendError, VmError
10 from xen.xend.XendLogging import log
11 from xen.xend import XendVnet
12 from xen.xend.XendRoot import get_component
14 import channel
15 from controller import CtrlMsgRcvr, Dev, DevController
16 from messages import *
18 class NetDev(Dev):
19 """A network device.
20 """
22 def __init__(self, controller, id, config, recreate=False):
23 Dev.__init__(self, controller, id, config, recreate=recreate)
24 self.vif = int(self.id)
25 self.evtchn = None
26 self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
27 self.frontendDomain = self.getDomain()
28 self.frontendChannel = None
29 self.backendDomain = None
30 self.backendChannel = None
31 self.credit = None
32 self.period = None
33 self.mac = None
34 self.be_mac = None
35 self.bridge = None
36 self.script = None
37 self.ipaddr = None
38 self.vifname = None
39 self.configure(self.config, recreate=recreate)
41 def init(self, recreate=False, reboot=False):
42 self.destroyed = False
43 self.frontendDomain = self.getDomain()
44 self.frontendChannel = self.getChannel()
45 cf = channel.channelFactory()
46 self.backendChannel = cf.openChannel(self.backendDomain)
48 def _get_config_mac(self, config):
49 vmac = sxp.child_value(config, 'mac')
50 if not vmac: return None
51 mac = [ int(x, 16) for x in vmac.split(':') ]
52 if len(mac) != 6: raise XendError("invalid mac: %s" % vmac)
53 return mac
55 def _get_config_be_mac(self, config):
56 vmac = sxp.child_value(config, 'be_mac')
57 if not vmac: return None
58 mac = [ int(x, 16) for x in vmac.split(':') ]
59 if len(mac) != 6: raise XendError("invalid backend mac: %s" % vmac)
60 return mac
62 def _get_config_ipaddr(self, config):
63 ips = sxp.children(config, elt='ip')
64 if ips:
65 val = []
66 for ipaddr in ips:
67 val.append(sxp.child0(ipaddr))
68 else:
69 val = None
70 return val
72 def configure(self, config, change=False, recreate=False):
73 if change:
74 return self.reconfigure(config)
75 self.config = config
76 self.mac = None
77 self.be_mac = None
78 self.bridge = None
79 self.script = None
80 self.ipaddr = []
81 self.vifname = None
83 self.vifname = sxp.child_value(config, 'vifname')
84 if self.vifname is None:
85 self.vifname = self.default_vifname()
86 if len(self.vifname) > 15:
87 raise XendError('invalid vifname: too long: ' + self.vifname)
88 mac = self._get_config_mac(config)
89 if mac is None:
90 raise XendError("invalid mac")
91 self.mac = mac
92 self.be_mac = self._get_config_be_mac(config)
93 self.bridge = sxp.child_value(config, 'bridge')
94 self.script = sxp.child_value(config, 'script')
95 self.ipaddr = self._get_config_ipaddr(config) or []
96 self._config_credit_limit(config)
98 try:
99 if recreate:
100 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
101 else:
102 #todo: Code below will fail on xend restart when backend is not domain 0.
103 xd = get_component('xen.xend.XendDomain')
104 self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
105 except:
106 raise XendError('invalid backend domain')
107 return self.config
109 def reconfigure(self, config):
110 """Reconfigure the interface with new values.
111 Not all configuration parameters can be changed:
112 bridge, script and ip addresses can,
113 backend and mac cannot.
115 To leave a parameter unchanged, omit it from the changes.
117 @param config configuration changes
118 @return updated interface configuration
119 @raise XendError on errors
120 """
121 changes = {}
122 mac = self._get_config_mac(config)
123 be_mac = self._get_config_be_mac(config)
124 bridge = sxp.child_value(config, 'bridge')
125 script = sxp.child_value(config, 'script')
126 ipaddr = self._get_config_ipaddr(config)
128 xd = get_component('xen.xend.XendDomain')
129 backendDomain = str(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
131 if (mac is not None) and (mac != self.mac):
132 raise XendError("cannot change mac")
133 if (be_mac is not None) and (be_mac != self.be_mac):
134 raise XendError("cannot change backend mac")
135 if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
136 raise XendError("cannot change backend")
137 if (bridge is not None) and (bridge != self.bridge):
138 changes['bridge'] = bridge
139 if (script is not None) and (script != self.script):
140 changes['script'] = script
141 if (ipaddr is not None) and (ipaddr != self.ipaddr):
142 changes['ipaddr'] = ipaddr
144 if changes:
145 self.vifctl("down")
146 for (k, v) in changes.items():
147 setattr(self, k, v)
148 self.config = sxp.merge(config, self.config)
149 self.vifctl("up")
151 self._config_credit_limit(config, change=True)
152 return self.config
154 def _config_credit_limit(self, config, change=False):
155 period = sxp.child_value(config, 'period')
156 credit = sxp.child_value(config, 'credit')
157 if period and credit:
158 try:
159 period = int(period)
160 credit = int(credit)
161 except ex:
162 raise XendError('vif: invalid credit limit')
163 if change:
164 self.setCreditLimit(credit, period)
165 self.config = sxp.merge([sxp.name(self.config),
166 ['credit', credit],
167 ['period', period]],
168 self.config)
169 else:
170 self.period = period
171 self.credit = credit
172 elif period or credit:
173 raise XendError('vif: invalid credit limit')
175 def sxpr(self):
176 vif = str(self.vif)
177 mac = self.get_mac()
178 val = ['vif',
179 ['id', self.id],
180 ['vif', vif],
181 ['mac', mac],
182 ['vifname', self.vifname],
183 ]
185 if self.be_mac:
186 val.append(['be_mac', self.get_be_mac()])
187 if self.bridge:
188 val.append(['bridge', self.bridge])
189 if self.script:
190 val.append(['script', self.script])
191 for ip in self.ipaddr:
192 val.append(['ip', ip])
193 if self.credit:
194 val.append(['credit', self.credit])
195 if self.period:
196 val.append(['period', self.period])
197 if self.evtchn:
198 val.append(['evtchn',
199 self.evtchn['port1'],
200 self.evtchn['port2']])
201 val.append(['index', self.getIndex()])
202 return val
204 def get_vifname(self):
205 """Get the virtual interface device name.
206 """
207 return self.vifname
209 def default_vifname(self):
210 return "vif%d.%d" % (self.frontendDomain, self.vif)
212 def get_mac(self):
213 """Get the MAC address as a string.
214 """
215 return ':'.join(map(lambda x: "%02x" % x, self.mac))
217 def get_be_mac(self):
218 """Get the backend MAC address as a string.
219 """
220 return ':'.join(map(lambda x: "%02x" % x, self.be_mac))
222 def vifctl_params(self, vmname=None):
223 """Get the parameters to pass to vifctl.
224 """
225 dom = self.frontendDomain
226 if vmname is None:
227 xd = get_component('xen.xend.XendDomain')
228 try:
229 vm = xd.domain_lookup(dom)
230 vmname = vm.name
231 except:
232 vmname = 'DOM%d' % dom
233 return { 'domain': vmname,
234 'vif' : self.get_vifname(),
235 'mac' : self.get_mac(),
236 'bridge': self.bridge,
237 'script': self.script,
238 'ipaddr': self.ipaddr, }
240 def vifctl(self, op, vmname=None):
241 """Bring the device up or down.
242 The vmname is needed when bringing a device up for a new domain because
243 the domain is not yet in the table so we can't look its name up.
245 @param op: operation name (up, down)
246 @param vmname: vmname
247 """
248 if op == 'up':
249 Vifctl.set_vif_name(self.default_vifname(), self.vifname)
250 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
251 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
252 if vnet:
253 vnet.vifctl(op, self.get_vifname(), self.get_mac())
255 def attach(self, recreate=False, change=False):
256 if recreate:
257 pass
258 else:
259 self.send_be_create()
260 if self.credit and self.period:
261 self.send_be_creditlimit(self.credit, self.period)
262 self.vifctl('up', vmname=self.getDomainName())
264 def closeEvtchn(self):
265 if self.evtchn:
266 channel.eventChannelClose(self.evtchn)
267 self.evtchn = None
269 def openEvtchn(self):
270 self.evtchn = channel.eventChannel(self.backendDomain, self.frontendDomain)
272 def getEventChannelBackend(self):
273 val = 0
274 if self.evtchn:
275 val = self.evtchn['port1']
276 return val
278 def getEventChannelFrontend(self):
279 val = 0
280 if self.evtchn:
281 val = self.evtchn['port2']
282 return val
284 def send_be_create(self):
285 msg = packMsg('netif_be_create_t',
286 { 'domid' : self.frontendDomain,
287 'netif_handle' : self.vif,
288 'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
289 'mac' : self.mac,
290 #'vifname' : self.vifname
291 })
292 msg = self.backendChannel.requestResponse(msg)
293 # todo: check return status
295 def destroy(self, change=False, reboot=False):
296 """Destroy the device's resources and disconnect from the back-end
297 device controller. If 'change' is true notify the front-end interface.
299 @param change: change flag
300 """
301 self.destroyed = True
302 self.status = NETIF_INTERFACE_STATUS_CLOSED
303 log.debug("Destroying vif domain=%d vif=%d", self.frontendDomain, self.vif)
304 self.closeEvtchn()
305 self.vifctl('down')
306 self.send_be_disconnect()
307 self.send_be_destroy()
308 if change:
309 self.reportStatus()
311 def send_be_disconnect(self):
312 msg = packMsg('netif_be_disconnect_t',
313 { 'domid' : self.frontendDomain,
314 'netif_handle' : self.vif })
315 return self.backendChannel.writeRequest(msg)
317 def send_be_destroy(self, response=None):
318 msg = packMsg('netif_be_destroy_t',
319 { 'domid' : self.frontendDomain,
320 'netif_handle' : self.vif })
321 return self.backendChannel.writeRequest(msg)
323 def recv_fe_interface_connect(self, val):
324 self.openEvtchn()
325 msg = packMsg('netif_be_connect_t',
326 { 'domid' : self.frontendDomain,
327 'netif_handle' : self.vif,
328 'evtchn' : self.getEventChannelBackend(),
329 'tx_shmem_frame' : val['tx_shmem_frame'],
330 'rx_shmem_frame' : val['rx_shmem_frame'] })
331 msg = self.backendChannel.requestResponse(msg)
332 #todo: check return status
333 self.status = NETIF_INTERFACE_STATUS_CONNECTED
334 self.reportStatus()
336 def setCreditLimit(self, credit, period):
337 #todo: these params should be in sxpr and vif config.
338 self.credit = credit
339 self.period = period
340 self.send_be_creditlimit(credit, period)
342 def getCredit(self):
343 return self.credit
345 def getPeriod(self):
346 return self.period
348 def send_be_creditlimit(self, credit, period):
349 msg = packMsg('netif_be_creditlimit_t',
350 { 'domid' : self.frontendDomain,
351 'netif_handle' : self.vif,
352 'credit_bytes' : credit,
353 'period_usec' : period })
354 msg = self.backendChannel.requestResponse(msg)
355 # todo: check return status
357 def reportStatus(self, resp=False):
358 msg = packMsg('netif_fe_interface_status_t',
359 { 'handle' : self.vif,
360 'status' : self.status,
361 'evtchn' : self.getEventChannelFrontend(),
362 'domid' : self.backendDomain,
363 'mac' : self.mac })
364 if resp:
365 self.frontendChannel.writeResponse(msg)
366 else:
367 self.frontendChannel.writeRequest(msg)
369 def interfaceChanged(self):
370 """Notify the front-end that a device has been added or removed.
371 """
372 self.reportStatus()
374 class NetifController(DevController):
375 """Network interface controller. Handles all network devices for a domain.
376 """
378 def __init__(self, dctype, vm, recreate=False):
379 DevController.__init__(self, dctype, vm, recreate=recreate)
380 self.channel = None
381 self.rcvr = None
382 self.channel = None
384 def initController(self, recreate=False, reboot=False):
385 self.destroyed = False
386 self.channel = self.getChannel()
387 # Register our handlers for incoming requests.
388 self.rcvr = CtrlMsgRcvr(self.channel)
389 self.rcvr.addHandler(CMSG_NETIF_FE,
390 CMSG_NETIF_FE_DRIVER_STATUS,
391 self.recv_fe_driver_status)
392 self.rcvr.addHandler(CMSG_NETIF_FE,
393 CMSG_NETIF_FE_INTERFACE_STATUS,
394 self.recv_fe_interface_status)
395 self.rcvr.addHandler(CMSG_NETIF_FE,
396 CMSG_NETIF_FE_INTERFACE_CONNECT,
397 self.recv_fe_interface_connect)
398 self.rcvr.registerChannel()
399 if reboot:
400 self.rebootDevices()
402 def destroyController(self, reboot=False):
403 """Destroy the controller and all devices.
404 """
405 self.destroyed = True
406 log.debug("Destroying netif domain=%d", self.getDomain())
407 self.destroyDevices(reboot=reboot)
408 if self.rcvr:
409 self.rcvr.deregisterChannel()
411 def sxpr(self):
412 val = ['netif', ['dom', self.getDomain()]]
413 return val
415 def newDevice(self, id, config, recreate=False):
416 """Create a network device.
418 @param id: interface id
419 @param config: device configuration
420 @param recreate: recreate flag (true after xend restart)
421 """
422 return NetDev(self, id, config, recreate=recreate)
424 def limitDevice(self, vif, credit, period):
425 if vif not in self.devices:
426 raise XendError('device does not exist for credit limit: vif'
427 + str(self.getDomain()) + '.' + str(vif))
429 dev = self.devices[vif]
430 return dev.setCreditLimit(credit, period)
432 def recv_fe_driver_status(self, msg):
433 msg = packMsg('netif_fe_driver_status_t',
434 { 'status' : NETIF_DRIVER_STATUS_UP,
435 ## FIXME: max_handle should be max active interface id
436 'max_handle' : self.getDeviceCount()
437 #'max_handle' : self.getMaxDeviceId()
438 })
439 # Two ways of doing it:
440 # 1) front-end requests driver status, we reply with the interface count,
441 # front-end polls the interfaces,
442 # front-end checks they are all up
443 # 2) front-end requests driver status, we reply (with anything),
444 # we notify the interfaces,
445 # we notify driver status up with the count
446 # front-end checks they are all up
447 #
448 # We really want to use 1), but at the moment the xenU kernel panics
449 # in that mode, so we're sticking to 2) for now.
450 resp = False
451 if resp:
452 self.channel.writeResponse(msg)
453 else:
454 for dev in self.devices.values():
455 dev.reportStatus()
456 self.channel.writeRequest(msg)
457 return resp
459 def recv_fe_interface_status(self, msg):
460 val = unpackMsg('netif_fe_interface_status_t', msg)
461 vif = val['handle']
462 dev = self.findDevice(vif)
463 if dev:
464 dev.reportStatus(resp=True)
465 else:
466 log.error('Received netif_fe_interface_status for unknown vif: dom=%d vif=%d',
467 self.getDomain(), vif)
468 msg = packMsg('netif_fe_interface_status_t',
469 { 'handle' : -1,
470 'status' : NETIF_INTERFACE_STATUS_CLOSED,
471 });
472 self.channel.writeResponse(msg)
473 return True
475 def recv_fe_interface_connect(self, msg):
476 val = unpackMsg('netif_fe_interface_connect_t', msg)
477 vif = val['handle']
478 dev = self.getDevice(vif)
479 if dev:
480 dev.recv_fe_interface_connect(val)
481 else:
482 log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
483 self.getDomain(), vif)