source: package_branches/invirt-web/cherrypy/code/main.py @ 2527

Last change on this file since 2527 was 2527, checked in by price, 15 years ago

tighten a bit of code

  • Property svn:executable set to *
File size: 28.5 KB
RevLine 
[113]1#!/usr/bin/python
[205]2"""Main CGI script for web interface"""
[113]3
[2449]4from __future__ import with_statement
5
[205]6import base64
7import cPickle
[113]8import cgi
[205]9import datetime
10import hmac
[770]11import random
[205]12import sha
13import sys
[2449]14import threading
[118]15import time
[447]16import urllib
[2186]17import socket
[2389]18import cherrypy
[2485]19from cherrypy import _cperror
[205]20from StringIO import StringIO
[2449]21
[205]22def printError():
23    """Revert stderr to stdout, and print the contents of stderr"""
24    if isinstance(sys.stderr, StringIO):
25        print revertStandardError()
26
27if __name__ == '__main__':
28    import atexit
29    atexit.register(printError)
30
[209]31import validation
[446]32import cache_acls
[1612]33from webcommon import State
[209]34import controls
[632]35from getafsgroups import getAfsGroupMembers
[865]36from invirt import database
[1001]37from invirt.database import Machine, CDROM, session, connect, MachineAccess, Type, Autoinstall
[863]38from invirt.config import structs as config
[1612]39from invirt.common import InvalidInput, CodeError
[2481]40import invirt.remctl
[113]41
[2485]42from view import View, revertStandardError
[2432]43import ajaxterm
[2390]44
[2484]45class InvirtUnauthWeb(View):
46    @cherrypy.expose
47    @cherrypy.tools.mako(filename="/unauth.mako")
48    def index(self):
49        return {'simple': True}
50
[2390]51class InvirtWeb(View):
52    def __init__(self):
53        super(self.__class__,self).__init__()
54        connect()
[2391]55        self._cp_config['tools.require_login.on'] = True
[2485]56        self._cp_config['tools.catch_stderr.on'] = True
[2403]57        self._cp_config['tools.mako.imports'] = ['from invirt.config import structs as config',
58                                                 'from invirt import database']
[2485]59        self._cp_config['request.error_response'] = self.handle_error
[2390]60
[2485]61    @cherrypy.expose
62    @cherrypy.tools.mako(filename="/invalid.mako")
63    def invalidInput(self):
64        """Print an error page when an InvalidInput exception occurs"""
65        err = cherrypy.request.prev.params["err"]
66        emsg = cherrypy.request.prev.params["emsg"]
67        d = dict(err_field=err.err_field,
68                 err_value=str(err.err_value), stderr=emsg,
69                 errorMessage=str(err))
70        return d
71
72    @cherrypy.expose
73    @cherrypy.tools.mako(filename="/error.mako")
74    def error(self):
75        """Print an error page when an exception occurs"""
76        op = cherrypy.request.prev.path_info
77        username = cherrypy.request.login
78        err = cherrypy.request.prev.params["err"]
79        emsg = cherrypy.request.prev.params["emsg"]
80        traceback = cherrypy.request.prev.params["traceback"]
[2523]81        d = dict(op=op, user=username, fields=cherrypy.request.prev.params,
[2485]82                 errorMessage=str(err), stderr=emsg, traceback=traceback)
[2486]83        error_raw = cherrypy.request.lookup.get_template("/error_raw.mako")
[2485]84        details = error_raw.render(**d)
85        exclude = config.web.errormail_exclude
86        if username not in exclude and '*' not in exclude:
87            send_error_mail('xvm error on %s for %s: %s' % (op, cherrypy.request.login, err),
88                            details)
89        d['details'] = details
90        return d
91
[2482]92    def __getattr__(self, name):
93        if name in ("admin", "overlord"):
94            if not cherrypy.request.login in getAfsGroupMembers(config.adminacl, config.authz[0].cell):
95                raise InvalidInput('username', cherrypy.request.login,
96                                   'Not in admin group %s.' % config.adminacl)
97            cherrypy.request.state = State(cherrypy.request.login, isadmin=True)
98            return self
99        else:
100            return super(InvirtWeb, self).__getattr__(name)
[2403]101
[2485]102    def handle_error(self):
103        err = sys.exc_info()[1]
104        if isinstance(err, InvalidInput):
105            cherrypy.request.params['err'] = err
[2524]106            cherrypy.request.params['emsg'] = revertStandardError()
[2485]107            raise cherrypy.InternalRedirect('/invalidInput')
108        if not cherrypy.request.prev or 'err' not in cherrypy.request.prev.params:
109            cherrypy.request.params['err'] = err
[2524]110            cherrypy.request.params['emsg'] = revertStandardError()
[2485]111            cherrypy.request.params['traceback'] = _cperror.format_exc()
112            raise cherrypy.InternalRedirect('/error')
113        # fall back to cherrypy default error page
114        cherrypy.HTTPError(500).set_response()
115
[2390]116    @cherrypy.expose
[2391]117    @cherrypy.tools.mako(filename="/list.mako")
[2418]118    def list(self, result=None):
[2391]119        """Handler for list requests."""
120        checkpoint.checkpoint('Getting list dict')
[2396]121        d = getListDict(cherrypy.request.login, cherrypy.request.state)
[2418]122        if result is not None:
123            d['result'] = result
[2391]124        checkpoint.checkpoint('Got list dict')
[2395]125        return d
[2391]126    index=list
127
128    @cherrypy.expose
[2409]129    @cherrypy.tools.mako(filename="/help.mako")
130    def help(self, subject=None, simple=False):
131        """Handler for help messages."""
132
133        help_mapping = {
134            'Autoinstalls': """
135The autoinstaller builds a minimal Debian or Ubuntu system to run as a
136ParaVM.  You can access the resulting system by logging into the <a
137href="help?simple=true&subject=ParaVM+Console">serial console server</a>
138with your Kerberos tickets; there is no root password so sshd will
139refuse login.</p>
140
141<p>Under the covers, the autoinstaller uses our own patched version of
142xen-create-image, which is a tool based on debootstrap.  If you log
143into the serial console while the install is running, you can watch
144it.
145""",
146            'ParaVM Console': """
147ParaVM machines do not support local console access over VNC.  To
148access the serial console of these machines, you can SSH with Kerberos
149to %s, using the name of the machine as your
150username.""" % config.console.hostname,
151            'HVM/ParaVM': """
152HVM machines use the virtualization features of the processor, while
153ParaVM machines rely on a modified kernel to communicate directly with
154the hypervisor.  HVMs support boot CDs of any operating system, and
155the VNC console applet.  The three-minute autoinstaller produces
156ParaVMs.  ParaVMs typically are more efficient, and always support the
157<a href="help?subject=ParaVM+Console">console server</a>.</p>
158
159<p>More details are <a
160href="https://xvm.scripts.mit.edu/wiki/Paravirtualization">on the
161wiki</a>, including steps to prepare an HVM guest to boot as a ParaVM
162(which you can skip by using the autoinstaller to begin with.)</p>
163
164<p>We recommend using a ParaVM when possible and an HVM when necessary.
165""",
166            'CPU Weight': """
167Don't ask us!  We're as mystified as you are.""",
168            'Owner': """
169The owner field is used to determine <a
170href="help?subject=Quotas">quotas</a>.  It must be the name of a
171locker that you are an AFS administrator of.  In particular, you or an
172AFS group you are a member of must have AFS rlidwka bits on the
173locker.  You can check who administers the LOCKER locker using the
174commands 'attach LOCKER; fs la /mit/LOCKER' on Athena.)  See also <a
175href="help?subject=Administrator">administrator</a>.""",
176            'Administrator': """
177The administrator field determines who can access the console and
178power on and off the machine.  This can be either a user or a moira
179group.""",
180            'Quotas': """
181Quotas are determined on a per-locker basis.  Each locker may have a
182maximum of 512 mebibytes of active ram, 50 gibibytes of disk, and 4
183active machines.""",
184            'Console': """
185<strong>Framebuffer:</strong> At a Linux boot prompt in your VM, try
186setting <tt>fb=false</tt> to disable the framebuffer.  If you don't,
187your machine will run just fine, but the applet's display of the
188console will suffer artifacts.
189""",
190            'Windows': """
191<strong>Windows Vista:</strong> The Vista image is licensed for all MIT students and will automatically activate off the network; see <a href="/static/msca-email.txt">the licensing confirmation e-mail</a> for details. The installer requires 512 MiB RAM and at least 7.5 GiB disk space (15 GiB or more recommended).<br>
[2423]192<strong>Windows XP:</strong> This is the volume license CD image. You will need your own volume license key to complete the install. We do not have these available for the general MIT community; ask your department if they have one, or visit <a href="http://msca.mit.edu/">http://msca.mit.edu/</a> if you are staff/faculty to request one.
[2409]193"""
194            }
195
196        if not subject:
197            subject = sorted(help_mapping.keys())
198        if not isinstance(subject, list):
199            subject = [subject]
200
[2410]201        return dict(simple=simple,
[2409]202                    subjects=subject,
203                    mapping=help_mapping)
204    help._cp_config['tools.require_login.on'] = False
205
[2422]206    def parseCreate(self, fields):
[2526]207        kws = dict([(kw, fields[kw]) for kw in
[2525]208         'name description owner memory disksize vmtype cdrom autoinstall'.split()
[2526]209                    if fields[kw]])
[2525]210        validate = validation.Validate(cherrypy.request.login,
211                                       cherrypy.request.state,
212                                       strict=True, **kws)
213        return dict(contact=cherrypy.request.login, name=validate.name,
214                    description=validate.description, memory=validate.memory,
215                    disksize=validate.disksize, owner=validate.owner,
216                    machine_type=getattr(validate, 'vmtype', Defaults.type),
[2422]217                    cdrom=getattr(validate, 'cdrom', None),
218                    autoinstall=getattr(validate, 'autoinstall', None))
219
[2409]220    @cherrypy.expose
[2422]221    @cherrypy.tools.mako(filename="/list.mako")
222    @cherrypy.tools.require_POST()
223    def create(self, **fields):
224        """Handler for create requests."""
225        try:
226            parsed_fields = self.parseCreate(fields)
[2525]227            machine = controls.createVm(cherrypy.request.login,
228                                        cherrypy.request.state, **parsed_fields)
[2422]229        except InvalidInput, err:
230            pass
231        else:
232            err = None
233        cherrypy.request.state.clear() #Changed global state
234        d = getListDict(cherrypy.request.login, cherrypy.request.state)
235        d['err'] = err
236        if err:
[2526]237            for field, value in fields.items():
238                setattr(d['defaults'], field, value))
[2422]239        else:
240            d['new_machine'] = parsed_fields['name']
241        return d
242
243    @cherrypy.expose
[2391]244    @cherrypy.tools.mako(filename="/helloworld.mako")
[2408]245    def helloworld(self, **kwargs):
246        return {'request': cherrypy.request, 'kwargs': kwargs}
[2391]247    helloworld._cp_config['tools.require_login.on'] = False
[2390]248
[2428]249    @cherrypy.expose
250    def errortest(self):
251        """Throw an error, to test the error-tracing mechanisms."""
[2485]252        print >>sys.stderr, "look ma, it's a stderr"
[2428]253        raise RuntimeError("test of the emergency broadcast system")
254
[2413]255    class MachineView(View):
256        # This is hairy. Fix when CherryPy 3.2 is out. (rename to
257        # _cp_dispatch, and parse the argument as a list instead of
258        # string
259
260        def __getattr__(self, name):
261            try:
[2527]262                cherrypy.request.params['machine_id'] = int(name)
[2413]263                return self
264            except ValueError:
265                return None
266
267        @cherrypy.expose
268        @cherrypy.tools.mako(filename="/info.mako")
269        def info(self, machine_id):
270            """Handler for info on a single VM."""
[2525]271            machine = validation.Validate(cherrypy.request.login,
272                                          cherrypy.request.state,
273                                          machine_id=machine_id).machine
[2413]274            d = infoDict(cherrypy.request.login, cherrypy.request.state, machine)
275            checkpoint.checkpoint('Got infodict')
276            return d
277        index = info
278
[2414]279        @cherrypy.expose
[2483]280        @cherrypy.tools.mako(filename="/info.mako")
281        @cherrypy.tools.require_POST()
282        def modify(self, machine_id, **fields):
283            """Handler for modifying attributes of a machine."""
284            try:
[2525]285                modify_dict = modifyDict(cherrypy.request.login,
286                                         cherrypy.request.state,
287                                         machine_id, fields)
[2483]288            except InvalidInput, err:
289                result = None
[2525]290                machine = validation.Validate(cherrypy.request.login,
291                                              cherrypy.request.state,
292                                              machine_id=machine_id).machine
[2483]293            else:
294                machine = modify_dict['machine']
295                result = 'Success!'
296                err = None
[2525]297            info_dict = infoDict(cherrypy.request.login,
298                                 cherrypy.request.state, machine)
[2483]299            info_dict['err'] = err
300            if err:
[2526]301                for field, value in fields.items():
302                    setattr(info_dict['defaults'], field, value)
[2483]303            info_dict['result'] = result
304            return info_dict
305
306        @cherrypy.expose
[2414]307        @cherrypy.tools.mako(filename="/vnc.mako")
308        def vnc(self, machine_id):
309            """VNC applet page.
310
311            Note that due to same-domain restrictions, the applet connects to
312            the webserver, which needs to forward those requests to the xen
313            server.  The Xen server runs another proxy that (1) authenticates
314            and (2) finds the correct port for the VM.
315
316            You might want iptables like:
317
318            -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
319            --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
320            -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
321            --dport 10003 -j SNAT --to-source 18.187.7.142
322            -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
323            --dport 10003 -j ACCEPT
324
325            Remember to enable iptables!
326            echo 1 > /proc/sys/net/ipv4/ip_forward
327            """
[2525]328            machine = validation.Validate(cherrypy.request.login,
329                                          cherrypy.request.state,
330                                          machine_id=machine_id).machine
[2414]331            token = controls.vnctoken(machine)
332            host = controls.listHost(machine)
333            if host:
334                port = 10003 + [h.hostname for h in config.hosts].index(host)
335            else:
336                port = 5900 # dummy
337
338            status = controls.statusInfo(machine)
339            has_vnc = hasVnc(status)
340
341            d = dict(on=status,
342                     has_vnc=has_vnc,
343                     machine=machine,
344                     hostname=cherrypy.request.local.name,
345                     port=port,
346                     authtoken=token)
347            return d
[2418]348        @cherrypy.expose
349        @cherrypy.tools.mako(filename="/command.mako")
[2422]350        @cherrypy.tools.require_POST()
[2418]351        def command(self, command_name, machine_id, **kwargs):
352            """Handler for running commands like boot and delete on a VM."""
353            back = kwargs.get('back', None)
354            try:
[2525]355                d = controls.commandResult(cherrypy.request.login,
356                                           cherrypy.request.state,
357                                           command_name, machine_id, kwargs)
[2418]358                if d['command'] == 'Delete VM':
359                    back = 'list'
360            except InvalidInput, err:
361                if not back:
362                    raise
363                print >> sys.stderr, err
[2485]364                result = str(err)
[2418]365            else:
366                result = 'Success!'
367                if not back:
368                    return d
369            if back == 'list':
370                cherrypy.request.state.clear() #Changed global state
[2525]371                raise cherrypy.InternalRedirect('/list?result=%s'
372                                                % urllib.quote(result))
[2418]373            elif back == 'info':
[2525]374                raise cherrypy.HTTPRedirect(cherrypy.request.base
375                                            + '/machine/%d/' % machine_id,
376                                            status=303)
[2418]377            else:
378                raise InvalidInput('back', back, 'Not a known back page.')
[2414]379
[2432]380        atmulti = ajaxterm.Multiplex()
381        atsessions = {}
[2449]382        atsessions_lock = threading.Lock()
[2432]383
384        @cherrypy.expose
385        @cherrypy.tools.mako(filename="/terminal.mako")
386        def terminal(self, machine_id):
387            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
388
389            status = controls.statusInfo(machine)
390            has_vnc = hasVnc(status)
391
392            d = dict(on=status,
393                     has_vnc=has_vnc,
394                     machine=machine,
395                     hostname=cherrypy.request.local.name)
396            return d
397
[2433]398        @cherrypy.expose
[2452]399        @cherrypy.tools.require_POST()
[2440]400        @cherrypy.tools.gzip()
[2454]401        def at(self, machine_id, k=None, c=0, h=None):
[2433]402            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
[2449]403            with self.atsessions_lock:
404                if machine_id in self.atsessions:
405                    term = self.atsessions[machine_id]
406                else:
407                    print >>sys.stderr, "spawning new session for terminal to ",machine_id
[2481]408                    invirt.remctl.checkKinit(principal='daemon/'+config.web.hostname)
[2450]409                    term = self.atmulti.create(
[2449]410                        ["ssh", "-e","none", "-l", machine.name, config.console.hostname]
411                        )
[2450]412                    # Clear out old sessions when fd is reused
413                    for key in self.atsessions:
414                        if self.atsessions[key] == term:
415                            del self.atsessions[key]
416                    self.atsessions[machine_id] = term
[2449]417                if k:
418                    self.atmulti.proc_write(term,k)
419                time.sleep(0.002)
[2454]420                dump=self.atmulti.dump(term,c,h)
[2449]421                cherrypy.response.headers['Content-Type']='text/xml'
422                if isinstance(dump,str):
423                    return dump
424                else:
[2481]425                    print "Removing session for", machine_id,"because we received",repr(dump)
[2449]426                    del self.atsessions[machine_id]
427                    return '<?xml version="1.0"?><idem></idem>'
[2433]428
[2413]429    machine = MachineView()
430
[235]431class Checkpoint:
432    def __init__(self):
433        self.start_time = time.time()
434        self.checkpoints = []
435
436    def checkpoint(self, s):
437        self.checkpoints.append((s, time.time()))
438
439    def __str__(self):
440        return ('Timing info:\n%s\n' %
441                '\n'.join(['%s: %s' % (d, t - self.start_time) for
442                           (d, t) in self.checkpoints]))
443
444checkpoint = Checkpoint()
445
[205]446class Defaults:
447    """Class to store default values for fields."""
448    memory = 256
449    disk = 4.0
450    cdrom = ''
[443]451    autoinstall = ''
[205]452    name = ''
[609]453    description = ''
[2483]454    administrator = ''
[515]455    type = 'linux-hvm'
456
[205]457    def __init__(self, max_memory=None, max_disk=None, **kws):
458        if max_memory is not None:
459            self.memory = min(self.memory, max_memory)
460        if max_disk is not None:
[1964]461            self.disk = min(self.disk, max_disk)
[205]462        for key in kws:
463            setattr(self, key, kws[key])
464
[119]465def hasVnc(status):
[133]466    """Does the machine with a given status list support VNC?"""
[119]467    if status is None:
468        return False
469    for l in status:
470        if l[0] == 'device' and l[1][0] == 'vfb':
471            d = dict(l[1][1:])
472            return 'location' in d
473    return False
474
[134]475
[572]476def getListDict(username, state):
[438]477    """Gets the list of local variables used by list.tmpl."""
[535]478    checkpoint.checkpoint('Starting')
[572]479    machines = state.machines
[235]480    checkpoint.checkpoint('Got my machines')
[133]481    on = {}
[119]482    has_vnc = {}
[2424]483    installing = {}
[572]484    xmlist = state.xmlist
[235]485    checkpoint.checkpoint('Got uptimes')
[136]486    for m in machines:
[535]487        if m not in xmlist:
[144]488            has_vnc[m] = 'Off'
[535]489            m.uptime = None
[136]490        else:
[535]491            m.uptime = xmlist[m]['uptime']
492            if xmlist[m]['console']:
493                has_vnc[m] = True
494            elif m.type.hvm:
495                has_vnc[m] = "WTF?"
496            else:
[2412]497                has_vnc[m] = "ParaVM"
[2424]498            if xmlist[m].get('autoinstall'):
499                installing[m] = True
500            else:
501                installing[m] = False
[572]502    max_memory = validation.maxMemory(username, state)
503    max_disk = validation.maxDisk(username)
[235]504    checkpoint.checkpoint('Got max mem/disk')
[205]505    defaults = Defaults(max_memory=max_memory,
506                        max_disk=max_disk,
[1739]507                        owner=username)
[235]508    checkpoint.checkpoint('Got defaults')
[424]509    def sortkey(machine):
[572]510        return (machine.owner != username, machine.owner, machine.name)
[424]511    machines = sorted(machines, key=sortkey)
[572]512    d = dict(user=username,
513             cant_add_vm=validation.cantAddVm(username, state),
[205]514             max_memory=max_memory,
[144]515             max_disk=max_disk,
[205]516             defaults=defaults,
[113]517             machines=machines,
[540]518             has_vnc=has_vnc,
[2424]519             installing=installing)
[205]520    return d
[113]521
[252]522def getHostname(nic):
[438]523    """Find the hostname associated with a NIC.
524
525    XXX this should be merged with the similar logic in DNS and DHCP.
526    """
[1976]527    if nic.hostname:
528        hostname = nic.hostname
[252]529    elif nic.machine:
[1976]530        hostname = nic.machine.name
[252]531    else:
532        return None
[1976]533    if '.' in hostname:
534        return hostname
535    else:
536        return hostname + '.' + config.dns.domains[0]
[252]537
[133]538def getNicInfo(data_dict, machine):
[145]539    """Helper function for info, get data on nics for a machine.
540
541    Modifies data_dict to include the relevant data, and returns a list
542    of (key, name) pairs to display "name: data_dict[key]" to the user.
543    """
[133]544    data_dict['num_nics'] = len(machine.nics)
[227]545    nic_fields_template = [('nic%s_hostname', 'NIC %s Hostname'),
[133]546                           ('nic%s_mac', 'NIC %s MAC Addr'),
547                           ('nic%s_ip', 'NIC %s IP'),
548                           ]
549    nic_fields = []
550    for i in range(len(machine.nics)):
551        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
[1976]552        data_dict['nic%s_hostname' % i] = getHostname(machine.nics[i])
[133]553        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
554        data_dict['nic%s_ip' % i] = machine.nics[i].ip
555    if len(machine.nics) == 1:
556        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
557    return nic_fields
558
559def getDiskInfo(data_dict, machine):
[145]560    """Helper function for info, get data on disks for a machine.
561
562    Modifies data_dict to include the relevant data, and returns a list
563    of (key, name) pairs to display "name: data_dict[key]" to the user.
564    """
[133]565    data_dict['num_disks'] = len(machine.disks)
566    disk_fields_template = [('%s_size', '%s size')]
567    disk_fields = []
568    for disk in machine.disks:
569        name = disk.guest_device_name
[438]570        disk_fields.extend([(x % name, y % name) for x, y in
[205]571                            disk_fields_template])
[211]572        data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
[133]573    return disk_fields
574
[2483]575def modifyDict(username, state, machine_id, fields):
[438]576    """Modify a machine as specified by CGI arguments.
577
[2483]578    Return a dict containing the machine that was modified.
[438]579    """
[177]580    olddisk = {}
[1013]581    session.begin()
[161]582    try:
[2526]583        kws = dict([(kw, fields[kw]) for kw in
[2525]584         'owner admin contact name description memory vmtype disksize'.split()
[2526]585                    if fields[kw]])
[2483]586        kws['machine_id'] = machine_id
[572]587        validate = validation.Validate(username, state, **kws)
588        machine = validate.machine
[161]589        oldname = machine.name
[153]590
[572]591        if hasattr(validate, 'memory'):
592            machine.memory = validate.memory
[438]593
[572]594        if hasattr(validate, 'vmtype'):
595            machine.type = validate.vmtype
[440]596
[572]597        if hasattr(validate, 'disksize'):
598            disksize = validate.disksize
[177]599            disk = machine.disks[0]
600            if disk.size != disksize:
601                olddisk[disk.guest_device_name] = disksize
602                disk.size = disksize
[1013]603                session.save_or_update(disk)
[438]604
[446]605        update_acl = False
[572]606        if hasattr(validate, 'owner') and validate.owner != machine.owner:
607            machine.owner = validate.owner
[446]608            update_acl = True
[572]609        if hasattr(validate, 'name'):
[586]610            machine.name = validate.name
[1977]611            for n in machine.nics:
612                if n.hostname == oldname:
613                    n.hostname = validate.name
[609]614        if hasattr(validate, 'description'):
615            machine.description = validate.description
[572]616        if hasattr(validate, 'admin') and validate.admin != machine.administrator:
617            machine.administrator = validate.admin
[446]618            update_acl = True
[572]619        if hasattr(validate, 'contact'):
620            machine.contact = validate.contact
[438]621
[1013]622        session.save_or_update(machine)
[446]623        if update_acl:
624            cache_acls.refreshMachine(machine)
[1013]625        session.commit()
[161]626    except:
[1013]627        session.rollback()
[163]628        raise
[177]629    for diskname in olddisk:
[209]630        controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
[572]631    if hasattr(validate, 'name'):
632        controls.renameMachine(machine, oldname, validate.name)
[2483]633    return dict(machine=machine)
[438]634
[579]635def infoDict(username, state, machine):
[438]636    """Get the variables used by info.tmpl."""
[209]637    status = controls.statusInfo(machine)
[235]638    checkpoint.checkpoint('Getting status info')
[133]639    has_vnc = hasVnc(status)
640    if status is None:
641        main_status = dict(name=machine.name,
642                           memory=str(machine.memory))
[205]643        uptime = None
644        cputime = None
[133]645    else:
646        main_status = dict(status[1:])
[662]647        main_status['host'] = controls.listHost(machine)
[167]648        start_time = float(main_status.get('start_time', 0))
649        uptime = datetime.timedelta(seconds=int(time.time()-start_time))
650        cpu_time_float = float(main_status.get('cpu_time', 0))
651        cputime = datetime.timedelta(seconds=int(cpu_time_float))
[235]652    checkpoint.checkpoint('Status')
[133]653    display_fields = [('name', 'Name'),
[609]654                      ('description', 'Description'),
[133]655                      ('owner', 'Owner'),
[187]656                      ('administrator', 'Administrator'),
[133]657                      ('contact', 'Contact'),
[136]658                      ('type', 'Type'),
[133]659                      'NIC_INFO',
660                      ('uptime', 'uptime'),
661                      ('cputime', 'CPU usage'),
[662]662                      ('host', 'Hosted on'),
[133]663                      ('memory', 'RAM'),
664                      'DISK_INFO',
665                      ('state', 'state (xen format)'),
666                      ]
667    fields = []
668    machine_info = {}
[147]669    machine_info['name'] = machine.name
[609]670    machine_info['description'] = machine.description
[136]671    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
[133]672    machine_info['owner'] = machine.owner
[187]673    machine_info['administrator'] = machine.administrator
[133]674    machine_info['contact'] = machine.contact
675
676    nic_fields = getNicInfo(machine_info, machine)
677    nic_point = display_fields.index('NIC_INFO')
[438]678    display_fields = (display_fields[:nic_point] + nic_fields +
[205]679                      display_fields[nic_point+1:])
[133]680
681    disk_fields = getDiskInfo(machine_info, machine)
682    disk_point = display_fields.index('DISK_INFO')
[438]683    display_fields = (display_fields[:disk_point] + disk_fields +
[205]684                      display_fields[disk_point+1:])
[438]685
[211]686    main_status['memory'] += ' MiB'
[133]687    for field, disp in display_fields:
[167]688        if field in ('uptime', 'cputime') and locals()[field] is not None:
[133]689            fields.append((disp, locals()[field]))
[147]690        elif field in machine_info:
691            fields.append((disp, machine_info[field]))
[133]692        elif field in main_status:
693            fields.append((disp, main_status[field]))
694        else:
695            pass
696            #fields.append((disp, None))
[235]697
698    checkpoint.checkpoint('Got fields')
699
700
[572]701    max_mem = validation.maxMemory(machine.owner, state, machine, False)
[235]702    checkpoint.checkpoint('Got mem')
[566]703    max_disk = validation.maxDisk(machine.owner, machine)
[209]704    defaults = Defaults()
[609]705    for name in 'machine_id name description administrator owner memory contact'.split():
[2483]706        if getattr(machine, name):
707            setattr(defaults, name, getattr(machine, name))
[516]708    defaults.type = machine.type.type_id
[205]709    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
[235]710    checkpoint.checkpoint('Got defaults')
[572]711    d = dict(user=username,
[133]712             on=status is not None,
713             machine=machine,
[205]714             defaults=defaults,
[133]715             has_vnc=has_vnc,
716             uptime=str(uptime),
717             ram=machine.memory,
[144]718             max_mem=max_mem,
719             max_disk=max_disk,
[133]720             fields = fields)
[205]721    return d
[113]722
[598]723def send_error_mail(subject, body):
724    import subprocess
[205]725
[863]726    to = config.web.errormail
[598]727    mail = """To: %s
[863]728From: root@%s
[598]729Subject: %s
730
731%s
[863]732""" % (to, config.web.hostname, subject, body)
[1718]733    p = subprocess.Popen(['/usr/sbin/sendmail', '-f', to, to],
734                         stdin=subprocess.PIPE)
[598]735    p.stdin.write(mail)
736    p.stdin.close()
737    p.wait()
738
[2485]739random.seed()
Note: See TracBrowser for help on using the repository browser.