| Trees | Indices | Help |
|---|
|
|
1 #
2 # Copyright (c) 2008--2014 Red Hat, Inc.
3 #
4 # This software is licensed to you under the GNU General Public License,
5 # version 2 (GPLv2). There is NO WARRANTY for this software, express or
6 # implied, including the implied warranties of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
8 # along with this software; if not, see
9 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
10 #
11 # Red Hat trademarks are not licensed under GPLv2. No permission is
12 # granted to use or replicate Red Hat trademarks that are incorporated
13 # in this software or its documentation.
14 #
15
16 import os
17 import subprocess
18 import sys
19
20 import binascii
21 import traceback
22 import gettext
23 t = gettext.translation('rhn-virtualization', fallback=True)
24 # Python 3 translations don't have a ugettext method
25 if not hasattr(t, 'ugettext'):
26 t.ugettext = t.gettext
27 _ = t.ugettext
28
29 from up2date_client import rhncli
30
31 try:
32 import libvirt
33 except ImportError:
34 # There might not be a libvirt.
35 libvirt = None
36
37 from optparse import OptionParser
38
39 from virtualization.state import State
40 from virtualization.errors import VirtualizationException
41 from virtualization.constants import PropertyType, \
42 VirtualizationType, \
43 IdentityType, \
44 VIRT_STATE_NAME_MAP, \
45 VIRT_VDSM_STATUS_MAP
46 from virtualization.notification import Plan, \
47 EventType, \
48 TargetType
49 from virtualization.util import hyphenize_uuid, \
50 is_fully_virt
51 from virtualization.poller_state_cache import PollerStateCache
52
53 from virtualization.domain_directory import DomainDirectory
54
55 from spacewalk.common.usix import raise_with_tb
56 ###############################################################################
57 # Globals
58 ###############################################################################
59
60 options = None
61
62 ###############################################################################
63 # Public Interface
64 ###############################################################################
65
67 """
68 This function polls the hypervisor for information about the currently
69 running set of domains. It returns a dictionary object that looks like the
70 following:
71
72 { uuid : { 'name' : '...',
73 'uuid' : '...',
74 'virt_type' : '...',
75 'memory_size' : '...',
76 'vcpus' : '...',
77 'state' : '...' }, ... }
78 """
79 if not libvirt:
80 return {}
81
82 try:
83 conn = libvirt.openReadOnly(None)
84 except libvirt.libvirtError:
85 # virConnectOpen() failed
86 sys.stderr.write(rhncli.utf8_encode(_("Warning: Could not retrieve virtualization information!\n\t" +
87 "libvirtd service needs to be running.\n")))
88 conn = None
89
90 if not conn:
91 # No connection to hypervisor made
92 return {}
93
94 domainIDs = conn.listDomainsID()
95
96 state = {}
97
98 for domainID in domainIDs:
99 try:
100 domain = conn.lookupByID(domainID)
101 except libvirt.libvirtError:
102 lve = sys.exc_info()[1]
103 raise_with_tb(VirtualizationException("Failed to obtain handle to domain %d: %s" % (domainID, repr(lve)),
104 sys.exc_info()[2]))
105
106 uuid = binascii.hexlify(domain.UUID())
107 # SEE: http://libvirt.org/html/libvirt-libvirt.html#virDomainInfo
108 # for more info.
109 domain_info = domain.info()
110
111 # Set the virtualization type. We can tell if the domain is fully virt
112 # by checking the domain's OSType() attribute.
113 virt_type = VirtualizationType.PARA
114 if is_fully_virt(domain):
115 virt_type = VirtualizationType.FULLY
116
117 # we need to filter out the small per/minute KB changes
118 # that occur inside a vm. To do this we divide by 1024 to
119 # drop our precision down to megabytes with an int then
120 # back up to KB
121 memory = int(domain_info[2] / 1024);
122 memory = memory * 1024;
123 properties = {
124 PropertyType.NAME : domain.name(),
125 PropertyType.UUID : uuid,
126 PropertyType.TYPE : virt_type,
127 PropertyType.MEMORY : str(memory), # current memory
128 PropertyType.VCPUS : domain_info[3],
129 PropertyType.STATE : VIRT_STATE_NAME_MAP[domain_info[0]] }
130
131 state[uuid] = properties
132
133 if state: _log_debug("Polled state: %s" % repr(state))
134
135 return state
136
138 """
139 This method polls all the virt guests running on a VDSM enabled Host.
140 Libvirt is disabled by default on RHEV-M managed clients.
141 * Imports the localvdsm client that talks to the localhost
142 and fetches the list of vms and their info.
143 * Extract the data and construct the state to pass it to the
144 execution plan for guest polling.
145 * The server should account for business rules similar to
146 xen/kvm.
147 """
148 # Extract list of vm's. True returns full list
149 try:
150 domains = server.list(True)
151 except:
152 # Something went wrong in vdsm, exit
153 return {}
154
155 if not len(domains['vmList']):
156 # No domains, exit.
157 return {}
158
159 state = {}
160 for domain in domains['vmList']:
161 #trim uuid
162 uuid = domain['vmId'].lower().replace('-', '')
163 # Map the VDSM status to libvirt for server compatibility
164 status = 'nostate'
165 if domain['status'] in VIRT_VDSM_STATUS_MAP:
166 status = VIRT_VDSM_STATUS_MAP[domain['status']]
167 # This is gonna be fully virt as its managed by VDSM
168 virt_type = VirtualizationType.FULLY
169
170 #Memory
171 memory = int(domain['memSize']) * 1024
172
173 # vcpus
174 if 'smp' in domain:
175 vcpus = domain['smp']
176 else:
177 vcpus = '1'
178
179 properties = {
180 PropertyType.NAME : domain['vmName'],
181 PropertyType.UUID : uuid,
182 PropertyType.TYPE : virt_type,
183 PropertyType.MEMORY : memory, # current memory
184 PropertyType.VCPUS : vcpus,
185 PropertyType.STATE : status}
186
187 state[uuid] = properties
188
189 if state: _log_debug("Polled state: %s" % repr(state))
190
191 return state
192
193
195 """
196 Polls just the state of the guest with the provided UUID. This state is
197 returned.
198 """
199 conn = libvirt.openReadOnly(None)
200 if not conn:
201 raise VirtualizationException("Failed to open connection to hypervisor.")
202
203 # Attempt to connect to the domain. Since there is technically no
204 # "stopped" state, we will assume that if we cannot connect the domain is
205 # not running. Unfortunately, we can't really determine if the domain
206 # actually exists.
207 domain = None
208 try:
209 domain = conn.lookupByUUIDString(hyphenize_uuid(uuid))
210 except libvirt.libvirtError:
211 # Can't find domain. Return stopped state.
212 return State(None)
213
214 # Now that we have the domain, lookup the state.
215 domain_info = domain.info()
216 return State(VIRT_STATE_NAME_MAP[domain_info[0]])
217
218 ###############################################################################
219 # Helper Functions
220 ###############################################################################
221
223 """
224 This function will send notifications based on vm state change to the
225 server. To reduce the possibility of spamming the server but still
226 maintain an element of consistency, it will compare the previous poll state
227 against the current poll state and only send notifications if something has
228 changed. In the event that the cache might have gotten into an
229 inconsistent state, the cache will be removed after every 50 polls (this is
230 about every 1.5 hours). This will cause the full state to be re-uploaded
231 and put things back in sync, if necessary.
232 """
233 # Now, if anything changed, send the appropriate notification for it.
234 if poller_state.is_changed():
235 added = poller_state.get_added()
236 removed = poller_state.get_removed()
237 modified = poller_state.get_modified()
238
239 plan = Plan()
240
241 # Declare virtualization host first
242 plan.add(EventType.EXISTS,
243 TargetType.SYSTEM,
244 { PropertyType.IDENTITY : IdentityType.HOST,
245 PropertyType.UUID : '0000000000000000' })
246
247 for (uuid, data) in added.items():
248 plan.add(EventType.EXISTS, TargetType.DOMAIN, data)
249
250 for (uuid, data) in modified.items():
251 plan.add(EventType.EXISTS, TargetType.DOMAIN, data)
252
253 for (uuid, data) in removed.items():
254 plan.add(EventType.REMOVED, TargetType.DOMAIN, data)
255
256 plan.execute()
257
259 usage = "Usage: %prog [options]"
260 parser = OptionParser(usage)
261 parser.set_defaults(debug=False)
262 parser.add_option("-d", "--debug", action="store_true", dest="debug")
263 global options
264 (options, args) = parser.parse_args()
265
272
273 ###############################################################################
274 # Main Program
275 ###############################################################################
276
277 if __name__ == "__main__":
278
279 # First, handle the options.
280 _parse_options()
281
282 vdsm_enabled = False
283 server = None
284 try:
285 from virtualization import localvdsm
286
287 status = subprocess.call(['/sbin/service','vdsmd','status'],
288 stdout=open(os.devnull, 'wb'),
289 stderr=subprocess.STDOUT)
290 if status == 0:
291 server = localvdsm.connect()
292 vdsm_enabled = True
293 except ImportError:
294 pass
295
296 # Crawl each of the domains on this host and obtain the new state.
297 if vdsm_enabled:
298 domain_list = poll_through_vdsm(server)
299 elif libvirt:
300 # If libvirt is present but not running, this program is useless.
301 # Libvirt currently writes to stderr if you attempt to connect
302 # and the daemon is not running. The libvirt python bindings provide
303 # no way to change this behavior. Anything written to stderr or stdout
304 # will cause cron to email root with the output. It is not our
305 # job to nag the user every two minutes if libvirt is not running.
306 # The only way to avoid this is to shell out to check if libvirt
307 # is running, and exit if it's not.
308 # See if the libvirtd service is running, discarding all output.
309 # Non-zero exit code means it's not running.
310 if (subprocess.call(['/sbin/service','libvirtd','status'],
311 stdout=open(os.devnull, 'wb'),
312 stderr=subprocess.STDOUT) != 0):
313 sys.exit(0)
314 domain_list = poll_hypervisor()
315 else:
316 # If no libvirt nor vdsm is present, this program is pretty much
317 # useless. Just exit.
318 sys.exit(0)
319
320 # create the unkonwn domain config files (for libvirt only)
321 if libvirt and not vdsm_enabled:
322 uuid_list = list(domain_list.keys())
323 domain = DomainDirectory()
324 domain.save_unknown_domain_configs(uuid_list)
325
326 cached_state = PollerStateCache(domain_list,
327 debug = options and options.debug)
328
329 # Send notifications, if necessary.
330 _send_notifications(cached_state)
331
332 # Save the new state.
333 cached_state.save()
334
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Wed Mar 4 07:37:42 2020 | http://epydoc.sourceforge.net |