Merge branch 'master' into lxc_devel
[nodemanager.git] / sliver_libvirt.py
1 #
2
3 """LibVirt slivers"""
4
5 import accounts
6 import logger
7 import subprocess
8 import os
9 import os.path
10 import libvirt
11 import sys
12 import shutil
13
14 from string import Template
15
16 STATES = {
17     libvirt.VIR_DOMAIN_NOSTATE: 'no state',
18     libvirt.VIR_DOMAIN_RUNNING: 'running',
19     libvirt.VIR_DOMAIN_BLOCKED: 'blocked on resource',
20     libvirt.VIR_DOMAIN_PAUSED: 'paused by user',
21     libvirt.VIR_DOMAIN_SHUTDOWN: 'being shut down',
22     libvirt.VIR_DOMAIN_SHUTOFF: 'shut off',
23     libvirt.VIR_DOMAIN_CRASHED: 'crashed',
24 }
25
26 REF_IMG_BASE_DIR = '/vservers/.lvref'
27 CON_BASE_DIR     = '/vservers'
28
29 connections = dict()
30
31 # Helper methods
32
33 def getConnection(sliver_type):
34     # TODO: error checking
35     # vtype is of the form sliver.[LXC/QEMU] we need to lower case to lxc/qemu
36     vtype = sliver_type.split('.')[1].lower()
37     uri = vtype + '://'
38     return connections.setdefault(uri, libvirt.open(uri))
39
40 def debuginfo(dom):
41     ''' Helper method to get a "nice" output of the info struct for debug'''
42     [state, maxmem, mem, ncpu, cputime] = dom.info()
43     return '%s is %s, maxmem = %s, mem = %s, ncpu = %s, cputime = %s' % (dom.name(), STATES.get(state, state), maxmem, mem, ncpu, cputime)
44
45 # Common Libvirt code
46
47 class Sliver_Libvirt(accounts.Account):
48
49     def __init__(self, rec):
50         self.name = rec['name']
51         logger.verbose ('sliver_libvirt: %s init'%(self.name))
52          
53         # Assume the directory with the image and config files
54         # are in place
55         
56         self.keys = ''
57         self.rspec = {}
58         self.slice_id = rec['slice_id']
59         self.enabled = True
60         self.conn = getConnection(rec['type'])
61         
62         try:
63             self.dom = self.conn.lookupByName(self.name)
64         except:
65             logger.verbose('sliver_libvirt: Domain %s does not exist UNEXPECTED: %s'%(self.name, sys.exc_info()[0]))
66
67
68     def start(self, delay=0):
69         ''' Just start the sliver '''
70         logger.verbose('sliver_libvirt: %s start'%(self.name))
71
72         # Check if it's running to avoid throwing an exception if the
73         # domain was already running, create actually means start
74         if not self.is_running():
75             self.dom.create()
76         else:
77             logger.verbose('sliver_libvirt: sliver %s already started'%(dom.name()))
78            
79
80     def stop(self):
81         logger.verbose('sliver_libvirt: %s stop'%(self.name))
82         
83         try:
84             self.dom.destroy()
85         except:
86             logger.verbose('sliver_libvirt: Domain %s not running UNEXPECTED: %s'%(self.name, sys.exc_info()[0]))
87             print 'sliver_libvirt: Domain %s not running UNEXPECTED: %s'%(self.name, sys.exc_info()[0])
88         
89     def is_running(self):
90         ''' Return True if the domain is running '''
91         logger.verbose('sliver_libvirt: %s is_running'%self.name)
92         try:
93             [state, _, _, _, _] = self.dom.info()
94             if state == libvirt.VIR_DOMAIN_RUNNING:
95                 logger.verbose('sliver_libvirt: %s is RUNNING'%self.name)
96                 return True
97             else:
98                 info = debuginfo(self.dom)
99                 logger.verbose('sliver_libvirt: %s is NOT RUNNING...\n%s'%(self.name, info))
100                 return False
101         except:
102             logger.verbose('sliver_libvirt: UNEXPECTED ERROR in %s...\n%s'%(self.name, sys.exc_info[0]))
103             print 'sliver_libvirt: UNEXPECTED ERROR in %s...\n%s'%(self.name, sys.exc_info[0])
104
105     def configure(self, rec):
106         
107         sliver_type = rec['type'].split('.')[1] #sliver.[lxc/qemu]
108
109         BASE_DIR = '/cgroup/libvirt/%s/%s/'%(sliver_type, self.name)
110         # Disk allocation
111         # No way through cgroups... figure out how to do that with user/dir quotas.
112         # There is no way to do quota per directory. Chown-ing would create
113         # problems as username namespaces are not yet implemented (and thus, host
114         # and containers share the same name ids
115
116         # Btrfs support quota per volumes
117
118         # It will depend on the FS selection
119         if rec.has_key('disk_max'):
120             disk_max = rec['disk_max']
121             if disk_max == 0:
122                 # unlimited 
123                 pass
124             else:
125                 # limit to certain number
126                 pass
127
128         # Memory allocation
129         if rec.has_key('memlock_hard'):
130             mem = rec['memlock_hard'] * 1024 # hard limit in bytes
131             with open(os.path.join(BASE_DIR, 'memory.limit_in_bytes'), 'w') as f:
132                 print >>f, mem
133         if rec.has_key('memlock_soft'):
134             mem = rec['memlock_soft'] * 1024 # soft limit in bytes
135             with open(os.path.join(BASE_DIR, 'memory.soft_limit_in_bytes'), 'w') as f:
136                 print >>f, mem
137
138         # CPU allocation
139         # Only cpu_shares until figure out how to provide limits and guarantees
140         # (RT_SCHED?)
141         if rec.has_key('cpu_share'): 
142             cpu_share = rec['cpu_share']
143             with open(os.path.join(BASE_DIR, 'cpu.shares'), 'w') as f:
144                 print >>f, cpu_share
145
146         # Call the upper configure method (ssh keys...)
147         accounts.Account.configure(self, rec)
148
149
150
151