ia64/xen-unstable

view tools/python/xen/xend/server/netif.py @ 6689:7d0fb56b4a91

merge?
author cl349@firebug.cl.cam.ac.uk
date Wed Sep 07 19:01:31 2005 +0000 (2005-09-07)
parents 0e2b1e04d4cb dd668f7527cb
children b2f4823b6ff0 b35215021b32 5cbb2ecce16a
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 #============================================================================
18 """Support for virtual network interfaces.
19 """
21 import random
23 from xen.util.mac import macFromString, macToString
25 from xen.xend import sxp
26 from xen.xend import Vifctl
27 from xen.xend.XendError import XendError, VmError
28 from xen.xend.XendLogging import log
29 from xen.xend import XendVnet
30 from xen.xend.XendRoot import get_component
31 from xen.xend.xenstore import DBVar
33 from xen.xend.server.controller import Dev, DevController
35 class NetDev(Dev):
36 """A network device.
37 """
39 # State:
40 # inherited +
41 # ./config
42 # ./mac
43 # ./be_mac
44 # ./bridge
45 # ./script
46 # ./ipaddr ?
47 #
48 # ./credit
49 # ./period
50 #
51 # ./vifctl: up/down?
52 # ./vifname
53 #
54 #
55 # Poss should have no backend state here - except for ref to backend's own tree
56 # for the device? And a status - the one we want.
57 # ./back/dom
58 # ./back/devid - id for back-end (netif_handle) - same as front/devid
59 # ./back/id - backend id (if more than one b/e per domain)
60 # ./back/status
61 # ./back/tx_shmem_frame - actually these belong in back-end state
62 # ./back/rx_shmem_frame
63 #
64 # ./front/dom
65 # ./front/devid
66 # ./front/status - need 2: one for requested, one for actual? Or drive from dev status
67 # and this is front status only.
68 # ./front/tx_shmem_frame
69 # ./front/rx_shmem_frame
70 #
71 # ./evtchn/front - here or in front/back?
72 # ./evtchn/back
73 # ./evtchn/status ?
74 # At present created by dev: but should be created unbound by front/back
75 # separately and then bound (by back)?
77 __exports__ = Dev.__exports__ + [
78 DBVar('config', ty='sxpr'),
79 DBVar('mac', ty='mac'),
80 DBVar('be_mac', ty='mac'),
81 DBVar('bridge', ty='str'),
82 DBVar('script', ty='str'),
83 DBVar('credit', ty='int'),
84 DBVar('period', ty='int'),
85 DBVar('vifname', ty='str'),
86 ]
88 def __init__(self, controller, id, config, recreate=False):
89 Dev.__init__(self, controller, id, config, recreate=recreate)
90 self.vif = int(self.id)
91 self.status = None
92 self.frontendDomain = self.getDomain()
93 self.backendDomain = None
94 self.credit = None
95 self.period = None
96 self.mac = None
97 self.be_mac = None
98 self.bridge = None
99 self.script = None
100 self.ipaddr = None
101 self.mtu = None
102 self.vifname = None
103 self.configure(self.config, recreate=recreate)
105 def exportToDB(self, save=False):
106 Dev.exportToDB(self, save=save)
108 def init(self, recreate=False, reboot=False):
109 self.destroyed = False
110 self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
111 self.frontendDomain = self.getDomain()
113 def _get_config_mac(self, config):
114 vmac = sxp.child_value(config, 'mac')
115 if not vmac: return None
116 try:
117 mac = macFromString(vmac)
118 except:
119 raise XendError("invalid mac: %s" % vmac)
120 return mac
122 def _get_config_be_mac(self, config):
123 vmac = sxp.child_value(config, 'be_mac')
124 if not vmac: return None
125 try:
126 mac = macFromString(vmac)
127 except:
128 raise XendError("invalid backend mac: %s" % vmac)
129 return mac
131 def _get_config_ipaddr(self, config):
132 ips = sxp.children(config, elt='ip')
133 if ips:
134 val = []
135 for ipaddr in ips:
136 val.append(sxp.child0(ipaddr))
137 else:
138 val = None
139 return val
141 def _get_config_mtu(self, config):
142 mtu = sxp.child_value(config, 'mtu')
143 if not mtu: return None
144 try:
145 mtu = int(mtu)
146 except:
147 raise XendError("invalid mtu: %s" & mtu)
148 return mtu
150 def configure(self, config, change=False, recreate=False):
151 if change:
152 return self.reconfigure(config)
153 self.config = config
154 self.mac = None
155 self.be_mac = None
156 self.bridge = None
157 self.script = None
158 self.ipaddr = []
159 self.vifname = None
161 self.vifname = sxp.child_value(config, 'vifname')
162 if self.vifname is None:
163 self.vifname = self.default_vifname()
164 if len(self.vifname) > 15:
165 raise XendError('invalid vifname: too long: ' + self.vifname)
166 mac = self._get_config_mac(config)
167 if mac is None:
168 raise XendError("invalid mac")
169 self.mac = mac
170 self.be_mac = self._get_config_be_mac(config)
171 self.bridge = sxp.child_value(config, 'bridge')
172 self.script = sxp.child_value(config, 'script')
173 self.ipaddr = self._get_config_ipaddr(config) or []
174 self.mtu = self._get_config_mtu(config)
175 self._config_credit_limit(config)
177 try:
178 if recreate:
179 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
180 else:
181 #todo: Code below will fail on xend restart when backend is not domain 0.
182 xd = get_component('xen.xend.XendDomain')
183 self.backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
184 except:
185 raise XendError('invalid backend domain')
186 return self.config
188 def reconfigure(self, config):
189 """Reconfigure the interface with new values.
190 Not all configuration parameters can be changed:
191 bridge, script and ip addresses can,
192 backend and mac cannot.
194 To leave a parameter unchanged, omit it from the changes.
196 @param config configuration changes
197 @return updated interface configuration
198 @raise XendError on errors
199 """
200 changes = {}
201 mac = self._get_config_mac(config)
202 be_mac = self._get_config_be_mac(config)
203 bridge = sxp.child_value(config, 'bridge')
204 script = sxp.child_value(config, 'script')
205 ipaddr = self._get_config_ipaddr(config)
206 mtu = self._get_config_mtu(config)
208 xd = get_component('xen.xend.XendDomain')
209 backendDomain = xd.domain_lookup_by_name(sxp.child_value(config, 'backend', '0')).id
211 if (mac is not None) and (mac != self.mac):
212 raise XendError("cannot change mac")
213 if (be_mac is not None) and (be_mac != self.be_mac):
214 raise XendError("cannot change backend mac")
215 if (backendDomain is not None) and (backendDomain != self.backendDomain):
216 raise XendError("cannot change backend")
217 if (bridge is not None) and (bridge != self.bridge):
218 changes['bridge'] = bridge
219 if (script is not None) and (script != self.script):
220 changes['script'] = script
221 if (ipaddr is not None) and (ipaddr != self.ipaddr):
222 changes['ipaddr'] = ipaddr
223 if (mtu is not None) and (mtu != self.mtu):
224 changes['mtu'] = mtu
226 if changes:
227 self.vifctl("down")
228 for (k, v) in changes.items():
229 setattr(self, k, v)
230 self.config = sxp.merge(config, self.config)
231 self.vifctl("up")
233 self._config_credit_limit(config, change=True)
234 return self.config
236 def _config_credit_limit(self, config, change=False):
237 period = sxp.child_value(config, 'period')
238 credit = sxp.child_value(config, 'credit')
239 if period and credit:
240 try:
241 period = int(period)
242 credit = int(credit)
243 except ex:
244 raise XendError('vif: invalid credit limit')
245 if change:
246 self.setCreditLimit(credit, period)
247 self.config = sxp.merge([sxp.name(self.config),
248 ['credit', credit],
249 ['period', period]],
250 self.config)
251 else:
252 self.period = period
253 self.credit = credit
254 elif period or credit:
255 raise XendError('vif: invalid credit limit')
257 def sxpr(self):
258 vif = str(self.vif)
259 mac = self.get_mac()
260 val = ['vif',
261 ['id', self.id],
262 ['vif', vif],
263 ['mac', mac],
264 ['vifname', self.vifname],
265 ]
267 if self.be_mac:
268 val.append(['be_mac', self.get_be_mac()])
269 if self.bridge:
270 val.append(['bridge', self.bridge])
271 if self.script:
272 val.append(['script', self.script])
273 for ip in self.ipaddr:
274 val.append(['ip', ip])
275 if self.credit:
276 val.append(['credit', self.credit])
277 if self.period:
278 val.append(['period', self.period])
279 return val
281 def get_vifname(self):
282 """Get the virtual interface device name.
283 """
284 return self.vifname
286 def default_vifname(self):
287 return "vif%d.%d" % (self.frontendDomain, self.vif)
289 def get_mac(self):
290 """Get the MAC address as a string.
291 """
292 return macToString(self.mac)
294 def get_be_mac(self):
295 """Get the backend MAC address as a string.
296 """
297 return macToString(self.be_mac)
299 def vifctl_params(self, vmname=None):
300 """Get the parameters to pass to vifctl.
301 """
302 dom = self.frontendDomain
303 if vmname is None:
304 xd = get_component('xen.xend.XendDomain')
305 try:
306 vm = xd.domain_lookup(dom)
307 vmname = vm.name
308 except:
309 vmname = 'Domain-%d' % dom
310 return { 'domain': vmname,
311 'vif' : self.get_vifname(),
312 'mac' : self.get_mac(),
313 'bridge': self.bridge,
314 'script': self.script,
315 'ipaddr': self.ipaddr, }
317 def vifctl(self, op, vmname=None):
318 """Bring the device up or down.
319 The vmname is needed when bringing a device up for a new domain because
320 the domain is not yet in the table so we can't look its name up.
322 @param op: operation name (up, down)
323 @param vmname: vmname
324 """
325 if op == 'up':
326 Vifctl.set_vif_name(self.default_vifname(), self.vifname)
327 Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
328 vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
329 if vnet:
330 vnet.vifctl(op, self.get_vifname(), self.get_mac())
332 def attach(self, recreate=False, change=False):
333 if recreate:
334 pass
335 else:
336 if self.credit and self.period:
337 #self.send_be_creditlimit(self.credit, self.period)
338 pass
339 self.vifctl('up', vmname=self.getDomainName())
341 def destroy(self, change=False, reboot=False):
342 """Destroy the device's resources and disconnect from the back-end
343 device controller. If 'change' is true notify the front-end interface.
345 @param change: change flag
346 """
347 self.destroyed = True
348 self.status = NETIF_INTERFACE_STATUS_CLOSED
349 log.debug("Destroying vif domain=%d vif=%d", self.frontendDomain, self.vif)
350 self.vifctl('down')
351 if change:
352 self.reportStatus()
354 def setCreditLimit(self, credit, period):
355 #todo: these params should be in sxpr and vif config.
356 self.credit = credit
357 self.period = period
359 def getCredit(self):
360 return self.credit
362 def getPeriod(self):
363 return self.period
365 def interfaceChanged(self):
366 """Notify the front-end that a device has been added or removed.
367 """
368 pass
370 class NetifController(DevController):
371 """Network interface controller. Handles all network devices for a domain.
372 """
374 def __init__(self, vm, recreate=False):
375 DevController.__init__(self, vm, recreate=recreate)
377 def initController(self, recreate=False, reboot=False):
378 self.destroyed = False
379 if reboot:
380 self.rebootDevices()
382 def destroyController(self, reboot=False):
383 """Destroy the controller and all devices.
384 """
385 self.destroyed = True
386 log.debug("Destroying netif domain=%d", self.getDomain())
387 self.destroyDevices(reboot=reboot)
389 def sxpr(self):
390 val = ['netif', ['dom', self.getDomain()]]
391 return val
393 def newDevice(self, id, config, recreate=False):
394 """Create a network device.
396 @param id: interface id
397 @param config: device configuration
398 @param recreate: recreate flag (true after xend restart)
399 """
400 return NetDev(self, id, config, recreate=recreate)
402 def limitDevice(self, vif, credit, period):
403 if vif not in self.devices:
404 raise XendError('device does not exist for credit limit: vif'
405 + str(self.getDomain()) + '.' + str(vif))
407 dev = self.devices[vif]
408 return dev.setCreditLimit(credit, period)