Revision c41eea6e
b/Makefile.am | ||
---|---|---|
344 | 344 |
CDIR=`pwd` && \ |
345 | 345 |
cd $$TMPDIR && \ |
346 | 346 |
mv lib ganeti && \ |
347 |
epydoc --conf $$CDIR/epydoc.conf -o $$CDIR/doc/api \ |
|
347 |
epydoc -v --conf $$CDIR/epydoc.conf -o $$CDIR/doc/api \
|
|
348 | 348 |
) ; \ |
349 | 349 |
rm -rf $$TMPDIR ; \ |
350 | 350 |
} |
b/daemons/ganeti-masterd | ||
---|---|---|
89 | 89 |
def __init__(self, address, rqhandler): |
90 | 90 |
"""IOServer constructor |
91 | 91 |
|
92 |
Args: |
|
93 |
address: the address to bind this IOServer to |
|
94 |
rqhandler: RequestHandler type object |
|
92 |
@param address: the address to bind this IOServer to |
|
93 |
@param rqhandler: RequestHandler type object |
|
95 | 94 |
|
96 | 95 |
""" |
97 | 96 |
SocketServer.UnixStreamServer.__init__(self, address, rqhandler) |
... | ... | |
348 | 347 |
def ParseOptions(): |
349 | 348 |
"""Parse the command line options. |
350 | 349 |
|
351 |
Returns: |
|
352 |
(options, args) as from OptionParser.parse_args() |
|
350 |
@return: (options, args) as from OptionParser.parse_args() |
|
353 | 351 |
|
354 | 352 |
""" |
355 | 353 |
parser = OptionParser(description="Ganeti master daemon", |
b/daemons/ganeti-noded | ||
---|---|---|
647 | 647 |
def ParseOptions(): |
648 | 648 |
"""Parse the command line options. |
649 | 649 |
|
650 |
Returns: |
|
651 |
(options, args) as from OptionParser.parse_args() |
|
650 |
@return: (options, args) as from OptionParser.parse_args() |
|
652 | 651 |
|
653 | 652 |
""" |
654 | 653 |
parser = OptionParser(description="Ganeti node daemon", |
b/daemons/ganeti-rapi | ||
---|---|---|
75 | 75 |
def ParseOptions(): |
76 | 76 |
"""Parse the command line options. |
77 | 77 |
|
78 |
Returns: |
|
79 |
(options, args) as from OptionParser.parse_args() |
|
78 |
@return: (options, args) as from OptionParser.parse_args() |
|
80 | 79 |
|
81 | 80 |
""" |
82 | 81 |
parser = optparse.OptionParser(description="Ganeti Remote API", |
b/daemons/ganeti-watcher | ||
---|---|---|
62 | 62 |
def Indent(s, prefix='| '): |
63 | 63 |
"""Indent a piece of text with a given prefix before each line. |
64 | 64 |
|
65 |
Args: |
|
66 |
s: The string to indent |
|
67 |
prefix: The string to prepend each line. |
|
65 |
@param s: the string to indent |
|
66 |
@param prefix: the string to prepend each line |
|
68 | 67 |
|
69 | 68 |
""" |
70 | 69 |
return "%s%s\n" % (prefix, ('\n' + prefix).join(s.splitlines())) |
... | ... | |
158 | 157 |
def NumberOfRestartAttempts(self, instance): |
159 | 158 |
"""Returns number of previous restart attempts. |
160 | 159 |
|
161 |
Args:
|
|
162 |
instance - the instance to look up.
|
|
160 |
@type instance: L{Instance}
|
|
161 |
@param instance: the instance to look up
|
|
163 | 162 |
|
164 | 163 |
""" |
165 | 164 |
idata = self._data["instance"] |
... | ... | |
172 | 171 |
def RecordRestartAttempt(self, instance): |
173 | 172 |
"""Record a restart attempt. |
174 | 173 |
|
175 |
Args:
|
|
176 |
instance - the instance being restarted
|
|
174 |
@type instance: L{Instance}
|
|
175 |
@param instance: the instance being restarted
|
|
177 | 176 |
|
178 | 177 |
""" |
179 | 178 |
idata = self._data["instance"] |
... | ... | |
187 | 186 |
inst[KEY_RESTART_COUNT] = inst.get(KEY_RESTART_COUNT, 0) + 1 |
188 | 187 |
|
189 | 188 |
def RemoveInstance(self, instance): |
190 |
"""Update state to reflect that a machine is running, i.e. remove record.
|
|
189 |
"""Update state to reflect that a machine is running. |
|
191 | 190 |
|
192 |
Args:
|
|
193 |
instance - the instance to remove from books
|
|
191 |
This method removes the record for a named instance (as we only
|
|
192 |
track down instances).
|
|
194 | 193 |
|
195 |
This method removes the record for a named instance. |
|
194 |
@type instance: L{Instance} |
|
195 |
@param instance: the instance to remove from books |
|
196 | 196 |
|
197 | 197 |
""" |
198 | 198 |
idata = self._data["instance"] |
... | ... | |
204 | 204 |
class Instance(object): |
205 | 205 |
"""Abstraction for a Virtual Machine instance. |
206 | 206 |
|
207 |
Methods: |
|
208 |
Restart(): issue a command to restart the represented machine. |
|
209 |
|
|
210 | 207 |
""" |
211 | 208 |
def __init__(self, name, state, autostart): |
212 | 209 |
self.name = name |
... | ... | |
399 | 396 |
def ParseOptions(): |
400 | 397 |
"""Parse the command line options. |
401 | 398 |
|
402 |
Returns: |
|
403 |
(options, args) as from OptionParser.parse_args() |
|
399 |
@return: (options, args) as from OptionParser.parse_args() |
|
404 | 400 |
|
405 | 401 |
""" |
406 | 402 |
parser = OptionParser(description="Ganeti cluster watcher", |
b/lib/backend.py | ||
---|---|---|
279 | 279 |
from the cluster. |
280 | 280 |
|
281 | 281 |
If processing is successful, then it raises an |
282 |
L{errors.GanetiQuitException} which is used as a special case to
|
|
282 |
L{errors.QuitGanetiException} which is used as a special case to
|
|
283 | 283 |
shutdown the node daemon. |
284 | 284 |
|
285 | 285 |
""" |
... | ... | |
970 | 970 |
|
971 | 971 |
@note: This is intended to be called recursively. |
972 | 972 |
|
973 |
@type disk: L{objects.disk}
|
|
973 |
@type disk: L{objects.Disk}
|
|
974 | 974 |
@param disk: the disk object we should remove |
975 | 975 |
@rtype: boolean |
976 | 976 |
@return: the success of the operation |
... | ... | |
1069 | 1069 |
def ShutdownBlockDevice(disk): |
1070 | 1070 |
"""Shut down a block device. |
1071 | 1071 |
|
1072 |
First, if the device is assembled (can L{Attach()}), then the device |
|
1073 |
is shutdown. Then the children of the device are shutdown. |
|
1072 |
First, if the device is assembled (Attach() is successfull), then |
|
1073 |
the device is shutdown. Then the children of the device are |
|
1074 |
shutdown. |
|
1074 | 1075 |
|
1075 | 1076 |
This function is called recursively. Note that we don't cache the |
1076 | 1077 |
children or such, as oppossed to assemble, shutdown of different |
... | ... | |
1161 | 1162 |
@rtype: disk |
1162 | 1163 |
@return: |
1163 | 1164 |
a list of (mirror_done, estimated_time) tuples, which |
1164 |
are the result of L{bdev.BlockDevice.CombinedSyncStatus}
|
|
1165 |
are the result of L{bdev.BlockDev.CombinedSyncStatus} |
|
1165 | 1166 |
@raise errors.BlockDeviceError: if any of the disks cannot be |
1166 | 1167 |
found |
1167 | 1168 |
|
... | ... | |
1981 | 1982 |
def JobQueueRename(old, new): |
1982 | 1983 |
"""Renames a job queue file. |
1983 | 1984 |
|
1984 |
This is just a wrapper over L{os.rename} with proper checking.
|
|
1985 |
This is just a wrapper over os.rename with proper checking.
|
|
1985 | 1986 |
|
1986 | 1987 |
@type old: str |
1987 | 1988 |
@param old: the old (actual) file name |
... | ... | |
2305 | 2306 |
node nor not |
2306 | 2307 |
@type iv_name: str |
2307 | 2308 |
@param iv_name: the instance-visible name of the |
2308 |
device, as in L{objects.Disk.iv_name}
|
|
2309 |
device, as in objects.Disk.iv_name
|
|
2309 | 2310 |
|
2310 | 2311 |
@rtype: None |
2311 | 2312 |
|
b/lib/bdev.py | ||
---|---|---|
202 | 202 |
If this device is a mirroring device, this function returns the |
203 | 203 |
status of the mirror. |
204 | 204 |
|
205 |
Returns: |
|
206 |
(sync_percent, estimated_time, is_degraded, ldisk) |
|
207 |
|
|
208 | 205 |
If sync_percent is None, it means the device is not syncing. |
209 | 206 |
|
210 | 207 |
If estimated_time is None, it means we can't estimate |
... | ... | |
218 | 215 |
data. This is only valid for some devices, the rest will always |
219 | 216 |
return False (not degraded). |
220 | 217 |
|
218 |
@rtype: tuple |
|
219 |
@return: (sync_percent, estimated_time, is_degraded, ldisk) |
|
220 |
|
|
221 | 221 |
""" |
222 | 222 |
return None, None, False, False |
223 | 223 |
|
... | ... | |
259 | 259 |
def Grow(self, amount): |
260 | 260 |
"""Grow the block device. |
261 | 261 |
|
262 |
Arguments: |
|
263 |
amount: the amount (in mebibytes) to grow with |
|
264 |
|
|
265 |
Returns: None |
|
262 |
@param amount: the amount (in mebibytes) to grow with |
|
266 | 263 |
|
267 | 264 |
""" |
268 | 265 |
raise NotImplementedError |
... | ... | |
326 | 323 |
def GetPVInfo(vg_name): |
327 | 324 |
"""Get the free space info for PVs in a volume group. |
328 | 325 |
|
329 |
Args: |
|
330 |
vg_name: the volume group name |
|
326 |
@param vg_name: the volume group name |
|
331 | 327 |
|
332 |
Returns:
|
|
333 |
list of (free_space, name) with free_space in mebibytes
|
|
328 |
@rtype: list
|
|
329 |
@return: list of tuples (free_space, name) with free_space in mebibytes
|
|
334 | 330 |
|
335 | 331 |
""" |
336 | 332 |
command = ["pvs", "--noheadings", "--nosuffix", "--units=m", |
... | ... | |
456 | 452 |
If this device is a mirroring device, this function returns the |
457 | 453 |
status of the mirror. |
458 | 454 |
|
459 |
Returns: |
|
460 |
(sync_percent, estimated_time, is_degraded, ldisk) |
|
461 |
|
|
462 | 455 |
For logical volumes, sync_percent and estimated_time are always |
463 | 456 |
None (no recovery in progress, as we don't handle the mirrored LV |
464 | 457 |
case). The is_degraded parameter is the inverse of the ldisk |
... | ... | |
472 | 465 |
|
473 | 466 |
The status was already read in Attach, so we just return it. |
474 | 467 |
|
468 |
@rtype: tuple |
|
469 |
@return: (sync_percent, estimated_time, is_degraded, ldisk) |
|
470 |
|
|
475 | 471 |
""" |
476 | 472 |
return None, None, self._degraded, self._degraded |
477 | 473 |
|
... | ... | |
642 | 638 |
def _MassageProcData(data): |
643 | 639 |
"""Transform the output of _GetProdData into a nicer form. |
644 | 640 |
|
645 |
Returns:
|
|
646 |
a dictionary of minor: joined lines from /proc/drbd for that minor
|
|
641 |
@return: a dictionary of minor: joined lines from /proc/drbd
|
|
642 |
for that minor
|
|
647 | 643 |
|
648 | 644 |
""" |
649 | 645 |
lmatch = re.compile("^ *([0-9]+):.*$") |
... | ... | |
669 | 665 |
"""Return the DRBD version. |
670 | 666 |
|
671 | 667 |
This will return a dict with keys: |
672 |
k_major,
|
|
673 |
k_minor,
|
|
674 |
k_point,
|
|
675 |
api,
|
|
676 |
proto,
|
|
677 |
proto2 (only on drbd > 8.2.X) |
|
668 |
- k_major
|
|
669 |
- k_minor
|
|
670 |
- k_point
|
|
671 |
- api
|
|
672 |
- proto
|
|
673 |
- proto2 (only on drbd > 8.2.X)
|
|
678 | 674 |
|
679 | 675 |
""" |
680 | 676 |
proc_data = cls._GetProcData() |
... | ... | |
1176 | 1172 |
def GetSyncStatus(self): |
1177 | 1173 |
"""Returns the sync status of the device. |
1178 | 1174 |
|
1179 |
Returns: |
|
1180 |
(sync_percent, estimated_time, is_degraded) |
|
1181 | 1175 |
|
1182 | 1176 |
If sync_percent is None, it means all is ok |
1183 | 1177 |
If estimated_time is None, it means we can't esimate |
... | ... | |
1190 | 1184 |
We compute the ldisk parameter based on wheter we have a local |
1191 | 1185 |
disk or not. |
1192 | 1186 |
|
1187 |
@rtype: tuple |
|
1188 |
@return: (sync_percent, estimated_time, is_degraded, ldisk) |
|
1189 |
|
|
1193 | 1190 |
""" |
1194 | 1191 |
if self.minor is None and not self.Attach(): |
1195 | 1192 |
raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus") |
... | ... | |
1504 | 1501 |
def Remove(self): |
1505 | 1502 |
"""Remove the file backing the block device. |
1506 | 1503 |
|
1507 |
Returns:
|
|
1508 |
boolean indicating wheter removal of file was successful or not.
|
|
1504 |
@rtype: boolean
|
|
1505 |
@return: True if the removal was successful
|
|
1509 | 1506 |
|
1510 | 1507 |
""" |
1511 | 1508 |
if not os.path.exists(self.dev_path): |
... | ... | |
1522 | 1519 |
|
1523 | 1520 |
Check if this file already exists. |
1524 | 1521 |
|
1525 |
Returns:
|
|
1526 |
boolean indicating if file exists or not.
|
|
1522 |
@rtype: boolean
|
|
1523 |
@return: True if file exists
|
|
1527 | 1524 |
|
1528 | 1525 |
""" |
1529 | 1526 |
self.attached = os.path.exists(self.dev_path) |
... | ... | |
1533 | 1530 |
def Create(cls, unique_id, children, size): |
1534 | 1531 |
"""Create a new file. |
1535 | 1532 |
|
1536 |
Args: |
|
1537 |
children: |
|
1538 |
size: integer size of file in MiB |
|
1533 |
@param size: the size of file in MiB |
|
1539 | 1534 |
|
1540 |
Returns:
|
|
1541 |
A ganeti.bdev.FileStorage object.
|
|
1535 |
@rtype: L{bdev.FileStorage}
|
|
1536 |
@return: an instance of FileStorage
|
|
1542 | 1537 |
|
1543 | 1538 |
""" |
1544 | 1539 |
if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
b/lib/bootstrap.py | ||
---|---|---|
41 | 41 |
def _InitSSHSetup(node): |
42 | 42 |
"""Setup the SSH configuration for the cluster. |
43 | 43 |
|
44 |
|
|
45 | 44 |
This generates a dsa keypair for root, adds the pub key to the |
46 | 45 |
permitted hosts and adds the hostkey to its own known hosts. |
47 | 46 |
|
48 |
Args: |
|
49 |
node: the name of this host as a fqdn |
|
47 |
@param node: the name of this host as an FQDN |
|
50 | 48 |
|
51 | 49 |
""" |
52 | 50 |
priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS) |
... | ... | |
243 | 241 |
node, and no instances. |
244 | 242 |
|
245 | 243 |
@type version: int |
246 |
@param version: Configuration version
|
|
247 |
@type cluster_config: objects.Cluster
|
|
248 |
@param cluster_config: Cluster configuration
|
|
249 |
@type master_node_config: objects.Node
|
|
250 |
@param master_node_config: Master node configuration
|
|
251 |
@type file_name: string
|
|
252 |
@param file_name: Configuration file path
|
|
253 |
|
|
254 |
@rtype: ssconf.SimpleConfigWriter
|
|
255 |
@returns: Initialized config instance
|
|
244 |
@param version: configuration version
|
|
245 |
@type cluster_config: L{objects.Cluster}
|
|
246 |
@param cluster_config: cluster configuration
|
|
247 |
@type master_node_config: L{objects.Node}
|
|
248 |
@param master_node_config: master node configuration
|
|
249 |
@type cfg_file: string
|
|
250 |
@param cfg_file: configuration file path
|
|
251 |
|
|
252 |
@rtype: L{ssconf.SimpleConfigWriter}
|
|
253 |
@returns: initialized config instance
|
|
256 | 254 |
|
257 | 255 |
""" |
258 | 256 |
nodes = { |
b/lib/cli.py | ||
---|---|---|
314 | 314 |
|
315 | 315 |
|
316 | 316 |
def _ParseArgs(argv, commands, aliases): |
317 |
"""Parses the command line and return the function which must be |
|
318 |
executed together with its arguments |
|
317 |
"""Parser for the command line arguments. |
|
319 | 318 |
|
320 |
Arguments:
|
|
321 |
argv: the command line
|
|
319 |
This function parses the arguements and returns the function which
|
|
320 |
must be executed together with its (modified) arguments.
|
|
322 | 321 |
|
323 |
commands: dictionary with special contents, see the design doc for |
|
324 |
cmdline handling |
|
325 |
aliases: dictionary with command aliases {'alias': 'target, ...} |
|
322 |
@param argv: the command line |
|
323 |
@param commands: dictionary with special contents, see the design |
|
324 |
doc for cmdline handling |
|
325 |
@param aliases: dictionary with command aliases {'alias': 'target, ...} |
|
326 | 326 |
|
327 | 327 |
""" |
328 | 328 |
if len(argv) == 0: |
... | ... | |
439 | 439 |
def AskUser(text, choices=None): |
440 | 440 |
"""Ask the user a question. |
441 | 441 |
|
442 |
Args: |
|
443 |
text - the question to ask. |
|
442 |
@param text: the question to ask |
|
444 | 443 |
|
445 |
choices - list with elements tuples (input_char, return_value,
|
|
446 |
description); if not given, it will default to: [('y', True, |
|
447 |
'Perform the operation'), ('n', False, 'Do no do the operation')]; |
|
448 |
note that the '?' char is reserved for help |
|
444 |
@param choices: list with elements tuples (input_char, return_value,
|
|
445 |
description); if not given, it will default to: [('y', True,
|
|
446 |
'Perform the operation'), ('n', False, 'Do no do the operation')];
|
|
447 |
note that the '?' char is reserved for help
|
|
449 | 448 |
|
450 |
Returns: one of the return values from the choices list; if input is
|
|
451 |
not possible (i.e. not running with a tty, we return the last entry
|
|
452 |
from the list |
|
449 |
@return: one of the return values from the choices list; if input is
|
|
450 |
not possible (i.e. not running with a tty, we return the last
|
|
451 |
entry from the list
|
|
453 | 452 |
|
454 | 453 |
""" |
455 | 454 |
if choices is None: |
b/lib/config.py | ||
---|---|---|
49 | 49 |
|
50 | 50 |
|
51 | 51 |
def _ValidateConfig(data): |
52 |
"""Verifies that a configuration objects looks valid. |
|
53 |
|
|
54 |
This only verifies the version of the configuration. |
|
55 |
|
|
56 |
@raise errors.ConfigurationError: if the version differs from what |
|
57 |
we expect |
|
58 |
|
|
59 |
""" |
|
52 | 60 |
if data.version != constants.CONFIG_VERSION: |
53 | 61 |
raise errors.ConfigurationError("Cluster configuration version" |
54 | 62 |
" mismatch, got %s instead of %s" % |
... | ... | |
155 | 163 |
This checks the current node, instances and disk names for |
156 | 164 |
duplicates. |
157 | 165 |
|
158 |
Args: |
|
159 |
- exceptions: a list with some other names which should be checked |
|
160 |
for uniqueness (used for example when you want to get |
|
161 |
more than one id at one time without adding each one in |
|
162 |
turn to the config file |
|
166 |
@param exceptions: a list with some other names which should be checked |
|
167 |
for uniqueness (used for example when you want to get |
|
168 |
more than one id at one time without adding each one in |
|
169 |
turn to the config file) |
|
163 | 170 |
|
164 |
Returns: the unique id as a string |
|
171 |
@rtype: string |
|
172 |
@return: the unique id |
|
165 | 173 |
|
166 | 174 |
""" |
167 | 175 |
existing = set() |
... | ... | |
185 | 193 |
def _AllMACs(self): |
186 | 194 |
"""Return all MACs present in the config. |
187 | 195 |
|
196 |
@rtype: list |
|
197 |
@return: the list of all MACs |
|
198 |
|
|
188 | 199 |
""" |
189 | 200 |
result = [] |
190 | 201 |
for instance in self._config_data.instances.values(): |
... | ... | |
196 | 207 |
def _AllDRBDSecrets(self): |
197 | 208 |
"""Return all DRBD secrets present in the config. |
198 | 209 |
|
210 |
@rtype: list |
|
211 |
@return: the list of all DRBD secrets |
|
212 |
|
|
199 | 213 |
""" |
200 | 214 |
def helper(disk, result): |
201 | 215 |
"""Recursively gather secrets from this disk.""" |
... | ... | |
377 | 391 |
def _ComputeDRBDMap(self, instance): |
378 | 392 |
"""Compute the used DRBD minor/nodes. |
379 | 393 |
|
380 |
Return: dictionary of node_name: dict of minor: instance_name. The
|
|
381 |
returned dict will have all the nodes in it (even if with an empty
|
|
382 |
list). |
|
394 |
@return: dictionary of node_name: dict of minor: instance_name;
|
|
395 |
the returned dict will have all the nodes in it (even if with
|
|
396 |
an empty list).
|
|
383 | 397 |
|
384 | 398 |
""" |
385 | 399 |
def _AppendUsedPorts(instance_name, disk, used): |
... | ... | |
521 | 535 |
def GetHostKey(self): |
522 | 536 |
"""Return the rsa hostkey from the config. |
523 | 537 |
|
524 |
Args: None |
|
538 |
@rtype: string |
|
539 |
@return: the rsa hostkey |
|
525 | 540 |
|
526 |
Returns: rsa hostkey |
|
527 | 541 |
""" |
528 | 542 |
return self._config_data.cluster.rsahostkeypub |
529 | 543 |
|
... | ... | |
533 | 547 |
|
534 | 548 |
This should be used after creating a new instance. |
535 | 549 |
|
536 |
Args: |
|
537 |
instance: the instance object |
|
550 |
@type instance: L{objects.Instance} |
|
551 |
@param instance: the instance object |
|
552 |
|
|
538 | 553 |
""" |
539 | 554 |
if not isinstance(instance, objects.Instance): |
540 | 555 |
raise errors.ProgrammerError("Invalid type passed to AddInstance") |
... | ... | |
628 | 643 |
def GetInstanceList(self): |
629 | 644 |
"""Get the list of instances. |
630 | 645 |
|
631 |
Returns: |
|
632 |
array of instances, ex. ['instance2.example.com','instance1.example.com'] |
|
633 |
these contains all the instances, also the ones in Admin_down state |
|
646 |
@return: array of instances, ex. ['instance2.example.com', |
|
647 |
'instance1.example.com'] |
|
634 | 648 |
|
635 | 649 |
""" |
636 | 650 |
return self._UnlockedGetInstanceList() |
... | ... | |
661 | 675 |
It takes the information from the configuration file. Other informations of |
662 | 676 |
an instance are taken from the live systems. |
663 | 677 |
|
664 |
Args:
|
|
665 |
instance: name of the instance, ex instance1.example.com
|
|
678 |
@param instance_name: name of the instance, e.g.
|
|
679 |
I{instance1.example.com}
|
|
666 | 680 |
|
667 |
Returns:
|
|
668 |
the instance object
|
|
681 |
@rtype: L{objects.Instance}
|
|
682 |
@return: the instance object
|
|
669 | 683 |
|
670 | 684 |
""" |
671 | 685 |
return self._UnlockedGetInstanceInfo(instance_name) |
... | ... | |
687 | 701 |
def AddNode(self, node): |
688 | 702 |
"""Add a node to the configuration. |
689 | 703 |
|
690 |
Args:
|
|
691 |
node: an object.Node instance
|
|
704 |
@type node: L{objects.Node}
|
|
705 |
@param node: a Node instance
|
|
692 | 706 |
|
693 | 707 |
""" |
694 | 708 |
logging.info("Adding node %s to configuration" % node.name) |
... | ... | |
723 | 737 |
def _UnlockedGetNodeInfo(self, node_name): |
724 | 738 |
"""Get the configuration of a node, as stored in the config. |
725 | 739 |
|
726 |
This function is for internal use, when the config lock is already held. |
|
740 |
This function is for internal use, when the config lock is already |
|
741 |
held. |
|
727 | 742 |
|
728 |
Args: node: nodename (tuple) of the node
|
|
743 |
@param node_name: the node name, e.g. I{node1.example.com}
|
|
729 | 744 |
|
730 |
Returns: the node object |
|
745 |
@rtype: L{objects.Node} |
|
746 |
@return: the node object |
|
731 | 747 |
|
732 | 748 |
""" |
733 | 749 |
if node_name not in self._config_data.nodes: |
... | ... | |
740 | 756 |
def GetNodeInfo(self, node_name): |
741 | 757 |
"""Get the configuration of a node, as stored in the config. |
742 | 758 |
|
743 |
Args: node: nodename (tuple) of the node
|
|
759 |
This is just a locked wrapper over L{_UnlockedGetNodeInfo}.
|
|
744 | 760 |
|
745 |
Returns: the node object |
|
761 |
@param node_name: the node name, e.g. I{node1.example.com} |
|
762 |
|
|
763 |
@rtype: L{objects.Node} |
|
764 |
@return: the node object |
|
746 | 765 |
|
747 | 766 |
""" |
748 | 767 |
return self._UnlockedGetNodeInfo(node_name) |
... | ... | |
750 | 769 |
def _UnlockedGetNodeList(self): |
751 | 770 |
"""Return the list of nodes which are in the configuration. |
752 | 771 |
|
753 |
This function is for internal use, when the config lock is already held. |
|
772 |
This function is for internal use, when the config lock is already |
|
773 |
held. |
|
774 |
|
|
775 |
@rtype: list |
|
754 | 776 |
|
755 | 777 |
""" |
756 | 778 |
return self._config_data.nodes.keys() |
... | ... | |
846 | 868 |
def _OpenConfig(self): |
847 | 869 |
"""Read the config data from disk. |
848 | 870 |
|
849 |
In case we already have configuration data and the config file has |
|
850 |
the same mtime as when we read it, we skip the parsing of the |
|
851 |
file, since de-serialisation could be slow. |
|
852 |
|
|
853 | 871 |
""" |
854 | 872 |
f = open(self._cfg_file, 'r') |
855 | 873 |
try: |
... | ... | |
1026 | 1044 |
def GetClusterInfo(self): |
1027 | 1045 |
"""Returns informations about the cluster |
1028 | 1046 |
|
1029 |
Returns:
|
|
1030 |
the cluster object
|
|
1047 |
@rtype: L{objects.Cluster}
|
|
1048 |
@return: the cluster object
|
|
1031 | 1049 |
|
1032 | 1050 |
""" |
1033 | 1051 |
return self._config_data.cluster |
... | ... | |
1042 | 1060 |
that all modified objects will be saved, but the target argument |
1043 | 1061 |
is the one the caller wants to ensure that it's saved. |
1044 | 1062 |
|
1063 |
@param target: an instance of either L{objects.Cluster}, |
|
1064 |
L{objects.Node} or L{objects.Instance} which is existing in |
|
1065 |
the cluster |
|
1066 |
|
|
1045 | 1067 |
""" |
1046 | 1068 |
if self._config_data is None: |
1047 | 1069 |
raise errors.ProgrammerError("Configuration file not read," |
b/lib/errors.py | ||
---|---|---|
228 | 228 |
error should returned to the caller, and the second one will be the returned |
229 | 229 |
result (either as an error or as a normal result). |
230 | 230 |
|
231 |
Examples: |
|
231 |
Examples:: |
|
232 |
|
|
232 | 233 |
# Return a result of "True" to the caller, but quit ganeti afterwards |
233 | 234 |
raise QuitGanetiException(False, True) |
234 | 235 |
# Send an error to the caller, and quit ganeti |
b/lib/http/server.py | ||
---|---|---|
393 | 393 |
|
394 | 394 |
@type mainloop: ganeti.daemon.Mainloop |
395 | 395 |
@param mainloop: Mainloop used to poll for I/O events |
396 |
@type local_addess: string |
|
396 |
@type local_address: string
|
|
397 | 397 |
@param local_address: Local IP address to bind to |
398 | 398 |
@type port: int |
399 | 399 |
@param port: TCP port to listen on |
b/lib/hypervisor/hv_base.py | ||
---|---|---|
57 | 57 |
def GetInstanceInfo(self, instance_name): |
58 | 58 |
"""Get instance properties. |
59 | 59 |
|
60 |
Args: |
|
61 |
instance_name: the instance name |
|
60 |
@param instance_name: the instance name |
|
62 | 61 |
|
63 |
Returns: |
|
64 |
(name, id, memory, vcpus, state, times) |
|
62 |
@return: tuple (name, id, memory, vcpus, state, times) |
|
65 | 63 |
|
66 | 64 |
""" |
67 | 65 |
raise NotImplementedError |
... | ... | |
69 | 67 |
def GetAllInstancesInfo(self): |
70 | 68 |
"""Get properties of all instances. |
71 | 69 |
|
72 |
Returns:
|
|
73 |
[(name, id, memory, vcpus, stat, times),...] |
|
70 |
@return: list of tuples (name, id, memory, vcpus, stat, times)
|
|
71 |
|
|
74 | 72 |
""" |
75 | 73 |
raise NotImplementedError |
76 | 74 |
|
77 | 75 |
def GetNodeInfo(self): |
78 | 76 |
"""Return information about the node. |
79 | 77 |
|
80 |
The return value is a dict, which has to have the following items: |
|
81 |
(all values in MiB) |
|
82 |
- memory_total: the total memory size on the node |
|
83 |
- memory_free: the available memory on the node for instances |
|
84 |
- memory_dom0: the memory used by the node itself, if available |
|
78 |
@return: a dict with the following keys (values in MiB): |
|
79 |
- memory_total: the total memory size on the node |
|
80 |
- memory_free: the available memory on the node for instances |
|
81 |
- memory_dom0: the memory used by the node itself, if available |
|
85 | 82 |
|
86 | 83 |
""" |
87 | 84 |
raise NotImplementedError |
b/lib/hypervisor/hv_fake.py | ||
---|---|---|
56 | 56 |
def GetInstanceInfo(self, instance_name): |
57 | 57 |
"""Get instance properties. |
58 | 58 |
|
59 |
Args: |
|
60 |
instance_name: the instance name |
|
59 |
@param instance_name: the instance name |
|
60 |
|
|
61 |
@return: tuple of (name, id, memory, vcpus, stat, times) |
|
61 | 62 |
|
62 |
Returns: |
|
63 |
(name, id, memory, vcpus, stat, times) |
|
64 | 63 |
""" |
65 | 64 |
file_name = "%s/%s" % (self._ROOT_DIR, instance_name) |
66 | 65 |
if not os.path.exists(file_name): |
... | ... | |
83 | 82 |
def GetAllInstancesInfo(self): |
84 | 83 |
"""Get properties of all instances. |
85 | 84 |
|
86 |
Returns:
|
|
87 |
[(name, id, memory, vcpus, stat, times),...] |
|
85 |
@return: list of tuples (name, id, memory, vcpus, stat, times)
|
|
86 |
|
|
88 | 87 |
""" |
89 | 88 |
data = [] |
90 | 89 |
for file_name in os.listdir(self._ROOT_DIR): |
... | ... | |
155 | 154 |
def GetNodeInfo(self): |
156 | 155 |
"""Return information about the node. |
157 | 156 |
|
158 |
The return value is a dict, which has to have the following items: |
|
159 |
(all values in MiB) |
|
160 |
- memory_total: the total memory size on the node |
|
161 |
- memory_free: the available memory on the node for instances |
|
162 |
- memory_dom0: the memory used by the node itself, if available |
|
157 |
@return: a dict with the following keys (values in MiB): |
|
158 |
- memory_total: the total memory size on the node |
|
159 |
- memory_free: the available memory on the node for instances |
|
160 |
- memory_dom0: the memory used by the node itself, if available |
|
163 | 161 |
|
164 | 162 |
""" |
165 | 163 |
# global ram usage from the xm info command |
b/lib/hypervisor/hv_kvm.py | ||
---|---|---|
105 | 105 |
def ListInstances(self): |
106 | 106 |
"""Get the list of running instances. |
107 | 107 |
|
108 |
We can do this by listing our live instances directory and checking whether
|
|
109 |
the associated kvm process is still alive. |
|
108 |
We can do this by listing our live instances directory and |
|
109 |
checking whether the associated kvm process is still alive.
|
|
110 | 110 |
|
111 | 111 |
""" |
112 | 112 |
result = [] |
... | ... | |
119 | 119 |
def GetInstanceInfo(self, instance_name): |
120 | 120 |
"""Get instance properties. |
121 | 121 |
|
122 |
Args: |
|
123 |
instance_name: the instance name |
|
122 |
@param instance_name: the instance name |
|
123 |
|
|
124 |
@return: tuple (name, id, memory, vcpus, stat, times) |
|
124 | 125 |
|
125 |
Returns: |
|
126 |
(name, id, memory, vcpus, stat, times) |
|
127 | 126 |
""" |
128 | 127 |
pidfile = "%s/%s" % (self._PIDS_DIR, instance_name) |
129 | 128 |
pid = utils.ReadPidFile(pidfile) |
... | ... | |
159 | 158 |
def GetAllInstancesInfo(self): |
160 | 159 |
"""Get properties of all instances. |
161 | 160 |
|
162 |
Returns:
|
|
163 |
[(name, id, memory, vcpus, stat, times),...] |
|
161 |
@return: list of tuples (name, id, memory, vcpus, stat, times)
|
|
162 |
|
|
164 | 163 |
""" |
165 | 164 |
data = [] |
166 | 165 |
for name in os.listdir(self._PIDS_DIR): |
... | ... | |
288 | 287 |
def GetNodeInfo(self): |
289 | 288 |
"""Return information about the node. |
290 | 289 |
|
291 |
The return value is a dict, which has to have the following items: |
|
292 |
(all values in MiB) |
|
293 |
- memory_total: the total memory size on the node |
|
294 |
- memory_free: the available memory on the node for instances |
|
295 |
- memory_dom0: the memory used by the node itself, if available |
|
290 |
@return: a dict with the following keys (values in MiB): |
|
291 |
- memory_total: the total memory size on the node |
|
292 |
- memory_free: the available memory on the node for instances |
|
293 |
- memory_dom0: the memory used by the node itself, if available |
|
296 | 294 |
|
297 | 295 |
""" |
298 | 296 |
# global ram usage from the xm info command |
b/lib/hypervisor/hv_xen.py | ||
---|---|---|
61 | 61 |
def _GetXMList(include_node): |
62 | 62 |
"""Return the list of running instances. |
63 | 63 |
|
64 |
If the `include_node` argument is True, then we return information
|
|
64 |
If the include_node argument is True, then we return information
|
|
65 | 65 |
for dom0 also, otherwise we filter that from the return value. |
66 | 66 |
|
67 |
The return value is a list of (name, id, memory, vcpus, state, time spent)
|
|
67 |
@return: list of (name, id, memory, vcpus, state, time spent)
|
|
68 | 68 |
|
69 | 69 |
""" |
70 | 70 |
for dummy in range(5): |
... | ... | |
117 | 117 |
def GetInstanceInfo(self, instance_name): |
118 | 118 |
"""Get instance properties. |
119 | 119 |
|
120 |
Args: |
|
121 |
instance_name: the instance name |
|
120 |
@param instance_name: the instance name |
|
121 |
|
|
122 |
@return: tuple (name, id, memory, vcpus, stat, times) |
|
122 | 123 |
|
123 |
Returns: |
|
124 |
(name, id, memory, vcpus, stat, times) |
|
125 | 124 |
""" |
126 | 125 |
xm_list = self._GetXMList(instance_name=="Domain-0") |
127 | 126 |
result = None |
... | ... | |
134 | 133 |
def GetAllInstancesInfo(self): |
135 | 134 |
"""Get properties of all instances. |
136 | 135 |
|
137 |
Returns:
|
|
138 |
[(name, id, memory, vcpus, stat, times),...] |
|
136 |
@return: list of tuples (name, id, memory, vcpus, stat, times)
|
|
137 |
|
|
139 | 138 |
""" |
140 | 139 |
xm_list = self._GetXMList(False) |
141 | 140 |
return xm_list |
142 | 141 |
|
143 | 142 |
def StartInstance(self, instance, block_devices, extra_args): |
144 |
"""Start an instance.""" |
|
143 |
"""Start an instance. |
|
144 |
|
|
145 |
""" |
|
145 | 146 |
self._WriteConfigFile(instance, block_devices, extra_args) |
146 | 147 |
result = utils.RunCmd(["xm", "create", instance.name]) |
147 | 148 |
|
... | ... | |
151 | 152 |
result.output)) |
152 | 153 |
|
153 | 154 |
def StopInstance(self, instance, force=False): |
154 |
"""Stop an instance.""" |
|
155 |
"""Stop an instance. |
|
156 |
|
|
157 |
""" |
|
155 | 158 |
self._RemoveConfigFile(instance) |
156 | 159 |
if force: |
157 | 160 |
command = ["xm", "destroy", instance.name] |
... | ... | |
164 | 167 |
(instance.name, result.fail_reason)) |
165 | 168 |
|
166 | 169 |
def RebootInstance(self, instance): |
167 |
"""Reboot an instance.""" |
|
170 |
"""Reboot an instance. |
|
171 |
|
|
172 |
""" |
|
168 | 173 |
result = utils.RunCmd(["xm", "reboot", instance.name]) |
169 | 174 |
|
170 | 175 |
if result.failed: |
... | ... | |
174 | 179 |
def GetNodeInfo(self): |
175 | 180 |
"""Return information about the node. |
176 | 181 |
|
177 |
The return value is a dict, which has to have the following items: |
|
178 |
(all values in MiB) |
|
179 |
- memory_total: the total memory size on the node |
|
180 |
- memory_free: the available memory on the node for instances |
|
181 |
- memory_dom0: the memory used by the node itself, if available |
|
182 |
@return: a dict with the following keys (values in MiB): |
|
183 |
- memory_total: the total memory size on the node |
|
184 |
- memory_free: the available memory on the node for instances |
|
185 |
- memory_dom0: the memory used by the node itself, if available |
|
182 | 186 |
|
183 | 187 |
""" |
184 | 188 |
# note: in xen 3, memory has changed to total_memory |
... | ... | |
233 | 237 |
This method builds the xen config disk directive according to the |
234 | 238 |
given disk_template and block_devices. |
235 | 239 |
|
236 |
Args: |
|
237 |
disk_template: String containing instance disk template |
|
238 |
block_devices: List[tuple1,tuple2,...] |
|
239 |
tuple: (cfdev, rldev) |
|
240 |
cfdev: dict containing ganeti config disk part |
|
241 |
rldev: ganeti.bdev.BlockDev object |
|
240 |
@param disk_template: string containing instance disk template |
|
241 |
@param block_devices: list of tuples (cfdev, rldev): |
|
242 |
- cfdev: dict containing ganeti config disk part |
|
243 |
- rldev: ganeti.bdev.BlockDev object |
|
242 | 244 |
|
243 |
Returns: |
|
244 |
String containing disk directive for xen instance config file |
|
245 |
@return: string containing disk directive for xen instance config file |
|
245 | 246 |
|
246 | 247 |
""" |
247 | 248 |
FILE_DRIVER_MAP = { |
b/lib/jqueue.py | ||
---|---|---|
1101 | 1101 |
"""Archives a job. |
1102 | 1102 |
|
1103 | 1103 |
@type job_id: string |
1104 |
@param job_id: Job ID of job to be archived.
|
|
1104 |
@param job_id: the ID of job to be archived
|
|
1105 | 1105 |
|
1106 | 1106 |
""" |
1107 | 1107 |
logging.info("Archiving job %s", job_id) |
b/lib/locking.py | ||
---|---|---|
104 | 104 |
def _is_owned(self, shared=-1): |
105 | 105 |
"""Is the current thread somehow owning the lock at this time? |
106 | 106 |
|
107 |
Args: |
|
108 |
shared: |
|
109 |
< 0: check for any type of ownership (default) |
|
110 |
0: check for exclusive ownership |
|
111 |
> 0: check for shared ownership |
|
107 |
@param shared: |
|
108 |
- < 0: check for any type of ownership (default) |
|
109 |
- 0: check for exclusive ownership |
|
110 |
- > 0: check for shared ownership |
|
112 | 111 |
|
113 | 112 |
""" |
114 | 113 |
self.__lock.acquire() |
... | ... | |
123 | 122 |
"""Wait on the given condition, and raise an exception if the current lock |
124 | 123 |
is declared deleted in the meantime. |
125 | 124 |
|
126 |
Args: |
|
127 |
c: condition to wait on |
|
125 |
@param c: the condition to wait on |
|
128 | 126 |
|
129 | 127 |
""" |
130 | 128 |
c.wait() |
... | ... | |
158 | 156 |
def acquire(self, blocking=1, shared=0): |
159 | 157 |
"""Acquire a shared lock. |
160 | 158 |
|
161 |
Args: |
|
162 |
shared: whether to acquire in shared mode. By default an exclusive lock |
|
163 |
will be acquired. |
|
164 |
blocking: whether to block while trying to acquire or to operate in |
|
165 |
try-lock mode. this locking mode is not supported yet. |
|
159 |
@param shared: whether to acquire in shared mode; by default an |
|
160 |
exclusive lock will be acquired |
|
161 |
@param blocking: whether to block while trying to acquire or to |
|
162 |
operate in try-lock mode (this locking mode is not supported yet) |
|
166 | 163 |
|
167 | 164 |
""" |
168 | 165 |
if not blocking: |
... | ... | |
268 | 265 |
acquired in exclusive mode if you don't already own it, then the lock |
269 | 266 |
will be put in a state where any future and pending acquire() fail. |
270 | 267 |
|
271 |
Args: |
|
272 |
blocking: whether to block while trying to acquire or to operate in |
|
273 |
try-lock mode. this locking mode is not supported yet unless |
|
274 |
you are already holding exclusively the lock. |
|
268 |
@param blocking: whether to block while trying to acquire or to |
|
269 |
operate in try-lock mode. this locking mode is not supported |
|
270 |
yet unless you are already holding exclusively the lock. |
|
275 | 271 |
|
276 | 272 |
""" |
277 | 273 |
self.__lock.acquire() |
... | ... | |
317 | 313 |
def __init__(self, members=None): |
318 | 314 |
"""Constructs a new LockSet. |
319 | 315 |
|
320 |
Args: |
|
321 |
members: initial members of the set |
|
316 |
@param members: initial members of the set |
|
322 | 317 |
|
323 | 318 |
""" |
324 | 319 |
# Used internally to guarantee coherency. |
... | ... | |
406 | 401 |
def acquire(self, names, blocking=1, shared=0): |
407 | 402 |
"""Acquire a set of resource locks. |
408 | 403 |
|
409 |
Args: |
|
410 |
names: the names of the locks which shall be acquired. |
|
411 |
(special lock names, or instance/node names) |
|
412 |
shared: whether to acquire in shared mode. By default an exclusive lock |
|
413 |
will be acquired. |
|
414 |
blocking: whether to block while trying to acquire or to operate in |
|
415 |
try-lock mode. this locking mode is not supported yet. |
|
404 |
@param names: the names of the locks which shall be acquired |
|
405 |
(special lock names, or instance/node names) |
|
406 |
@param shared: whether to acquire in shared mode; by default an |
|
407 |
exclusive lock will be acquired |
|
408 |
@param blocking: whether to block while trying to acquire or to |
|
409 |
operate in try-lock mode (this locking mode is not supported yet) |
|
416 | 410 |
|
417 |
Returns: |
|
418 |
True: when all the locks are successfully acquired |
|
411 |
@return: True when all the locks are successfully acquired |
|
419 | 412 |
|
420 |
Raises: |
|
421 |
errors.LockError: when any lock we try to acquire has been deleted |
|
422 |
before we succeed. In this case none of the locks requested will be |
|
423 |
acquired. |
|
413 |
@raise errors.LockError: when any lock we try to acquire has |
|
414 |
been deleted before we succeed. In this case none of the |
|
415 |
locks requested will be acquired. |
|
424 | 416 |
|
425 | 417 |
""" |
426 | 418 |
if not blocking: |
... | ... | |
520 | 512 |
You must have acquired the locks, either in shared or in exclusive mode, |
521 | 513 |
before releasing them. |
522 | 514 |
|
523 |
Args: |
|
524 |
names: the names of the locks which shall be released. |
|
525 |
(defaults to all the locks acquired at that level). |
|
515 |
@param names: the names of the locks which shall be released |
|
516 |
(defaults to all the locks acquired at that level). |
|
526 | 517 |
|
527 | 518 |
""" |
528 | 519 |
assert self._is_owned(), "release() on lock set while not owner" |
... | ... | |
554 | 545 |
def add(self, names, acquired=0, shared=0): |
555 | 546 |
"""Add a new set of elements to the set |
556 | 547 |
|
557 |
Args: |
|
558 |
names: names of the new elements to add |
|
559 |
acquired: pre-acquire the new resource? |
|
560 |
shared: is the pre-acquisition shared? |
|
548 |
@param names: names of the new elements to add |
|
549 |
@param acquired: pre-acquire the new resource? |
|
550 |
@param shared: is the pre-acquisition shared? |
|
561 | 551 |
|
562 | 552 |
""" |
563 | 553 |
# Check we don't already own locks at this level |
... | ... | |
616 | 606 |
You can either not hold anything in the lockset or already hold a superset |
617 | 607 |
of the elements you want to delete, exclusively. |
618 | 608 |
|
619 |
Args: |
|
620 |
names: names of the resource to remove. |
|
621 |
blocking: whether to block while trying to acquire or to operate in |
|
622 |
try-lock mode. this locking mode is not supported yet unless |
|
623 |
you are already holding exclusively the locks. |
|
609 |
@param names: names of the resource to remove. |
|
610 |
@param blocking: whether to block while trying to acquire or to |
|
611 |
operate in try-lock mode (this locking mode is not supported |
|
612 |
yet unless you are already holding exclusively the locks) |
|
624 | 613 |
|
625 |
Returns:
|
|
626 |
A list of lock which we removed. The list is always equal to the names
|
|
627 |
list if we were holding all the locks exclusively.
|
|
614 |
@return:: a list of locks which we removed; the list is always
|
|
615 |
equal to the names list if we were holding all the locks
|
|
616 |
exclusively
|
|
628 | 617 |
|
629 | 618 |
""" |
630 | 619 |
if not blocking and not self._is_owned(): |
... | ... | |
712 | 701 |
There should be only a GanetiLockManager object at any time, so this |
713 | 702 |
function raises an error if this is not the case. |
714 | 703 |
|
715 |
Args: |
|
716 |
nodes: list of node names |
|
717 |
instances: list of instance names |
|
704 |
@param nodes: list of node names |
|
705 |
@param instances: list of instance names |
|
718 | 706 |
|
719 | 707 |
""" |
720 |
assert self.__class__._instance is None, "double GanetiLockManager instance" |
|
708 |
assert self.__class__._instance is None, \ |
|
709 |
"double GanetiLockManager instance" |
|
710 |
|
|
721 | 711 |
self.__class__._instance = self |
722 | 712 |
|
723 | 713 |
# The keyring contains all the locks, at their level and in the correct |
... | ... | |
730 | 720 |
|
731 | 721 |
def _names(self, level): |
732 | 722 |
"""List the lock names at the given level. |
733 |
Used for debugging/testing purposes. |
|
734 | 723 |
|
735 |
Args: |
|
736 |
level: the level whose list of locks to get |
|
724 |
This can be used for debugging/testing purposes. |
|
725 |
|
|
726 |
@param level: the level whose list of locks to get |
|
737 | 727 |
|
738 | 728 |
""" |
739 | 729 |
assert level in LEVELS, "Invalid locking level %s" % level |
... | ... | |
770 | 760 |
return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned() |
771 | 761 |
|
772 | 762 |
def _contains_BGL(self, level, names): |
773 |
"""Check if acting on the given level and set of names will change the |
|
774 |
status of the Big Ganeti Lock. |
|
763 |
"""Check if the level contains the BGL. |
|
764 |
|
|
765 |
Check if acting on the given level and set of names will change |
|
766 |
the status of the Big Ganeti Lock. |
|
775 | 767 |
|
776 | 768 |
""" |
777 | 769 |
return level == LEVEL_CLUSTER and (names is None or BGL in names) |
... | ... | |
779 | 771 |
def acquire(self, level, names, blocking=1, shared=0): |
780 | 772 |
"""Acquire a set of resource locks, at the same level. |
781 | 773 |
|
782 |
Args: |
|
783 |
level: the level at which the locks shall be acquired. |
|
784 |
It must be a memmber of LEVELS. |
|
785 |
names: the names of the locks which shall be acquired. |
|
786 |
(special lock names, or instance/node names) |
|
787 |
shared: whether to acquire in shared mode. By default an exclusive lock |
|
788 |
will be acquired. |
|
789 |
blocking: whether to block while trying to acquire or to operate in |
|
790 |
try-lock mode. this locking mode is not supported yet. |
|
774 |
@param level: the level at which the locks shall be acquired; |
|
775 |
it must be a memmber of LEVELS. |
|
776 |
@param names: the names of the locks which shall be acquired |
|
777 |
(special lock names, or instance/node names) |
|
778 |
@param shared: whether to acquire in shared mode; by default |
|
779 |
an exclusive lock will be acquired |
|
780 |
@param blocking: whether to block while trying to acquire or to |
|
781 |
operate in try-lock mode (this locking mode is not supported yet) |
|
791 | 782 |
|
792 | 783 |
""" |
793 | 784 |
assert level in LEVELS, "Invalid locking level %s" % level |
... | ... | |
812 | 803 |
def release(self, level, names=None): |
813 | 804 |
"""Release a set of resource locks, at the same level. |
814 | 805 |
|
815 |
You must have acquired the locks, either in shared or in exclusive mode,
|
|
816 |
before releasing them. |
|
806 |
You must have acquired the locks, either in shared or in exclusive |
|
807 |
mode, before releasing them.
|
|
817 | 808 |
|
818 |
Args: |
|
819 |
level: the level at which the locks shall be released. |
|
820 |
It must be a memmber of LEVELS. |
|
821 |
names: the names of the locks which shall be released. |
|
822 |
(defaults to all the locks acquired at that level). |
|
809 |
@param level: the level at which the locks shall be released; |
|
810 |
it must be a memmber of LEVELS |
|
811 |
@param names: the names of the locks which shall be released |
|
812 |
(defaults to all the locks acquired at that level) |
|
823 | 813 |
|
824 | 814 |
""" |
825 | 815 |
assert level in LEVELS, "Invalid locking level %s" % level |
... | ... | |
834 | 824 |
def add(self, level, names, acquired=0, shared=0): |
835 | 825 |
"""Add locks at the specified level. |
836 | 826 |
|
837 |
Args:
|
|
838 |
level: the level at which the locks shall be added.
|
|
839 |
It must be a memmber of LEVELS_MOD.
|
|
840 |
names: names of the locks to acquire
|
|
841 |
acquired: whether to acquire the newly added locks
|
|
842 |
shared: whether the acquisition will be shared |
|
827 |
@param level: the level at which the locks shall be added;
|
|
828 |
it must be a memmber of LEVELS_MOD.
|
|
829 |
@param names: names of the locks to acquire
|
|
830 |
@param acquired: whether to acquire the newly added locks
|
|
831 |
@param shared: whether the acquisition will be shared
|
|
832 |
|
|
843 | 833 |
""" |
844 | 834 |
assert level in LEVELS_MOD, "Invalid or immutable level %s" % level |
845 | 835 |
assert self._BGL_owned(), ("You must own the BGL before performing other" |
... | ... | |
851 | 841 |
def remove(self, level, names, blocking=1): |
852 | 842 |
"""Remove locks from the specified level. |
853 | 843 |
|
854 |
You must either already own the locks you are trying to remove exclusively
|
|
855 |
or not own any lock at an upper level. |
|
844 |
You must either already own the locks you are trying to remove |
|
845 |
exclusively or not own any lock at an upper level.
|
|
856 | 846 |
|
857 |
Args: |
|
858 |
level: the level at which the locks shall be removed. |
|
859 |
It must be a memmber of LEVELS_MOD. |
|
860 |
names: the names of the locks which shall be removed. |
|
861 |
(special lock names, or instance/node names) |
|
862 |
blocking: whether to block while trying to operate in try-lock mode. |
|
863 |
this locking mode is not supported yet. |
|
847 |
@param level: the level at which the locks shall be removed; |
|
848 |
it must be a member of LEVELS_MOD |
|
849 |
@param names: the names of the locks which shall be removed |
|
850 |
(special lock names, or instance/node names) |
|
851 |
@param blocking: whether to block while trying to operate in |
|
852 |
try-lock mode (this locking mode is not supported yet) |
|
864 | 853 |
|
865 | 854 |
""" |
866 | 855 |
assert level in LEVELS_MOD, "Invalid or immutable level %s" % level |
b/lib/objects.py | ||
---|---|---|
549 | 549 |
def MapLVsByNode(self, lvmap=None, devs=None, node=None): |
550 | 550 |
"""Provide a mapping of nodes to LVs this instance owns. |
551 | 551 |
|
552 |
This function figures out what logical volumes should belong on which
|
|
553 |
nodes, recursing through a device tree. |
|
552 |
This function figures out what logical volumes should belong on |
|
553 |
which nodes, recursing through a device tree.
|
|
554 | 554 |
|
555 |
Args:
|
|
556 |
lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
|
|
555 |
@param lvmap: optional dictionary to receive the
|
|
556 |
'node' : ['lv', ...] data.
|
|
557 | 557 |
|
558 |
Returns: |
|
559 |
None if lvmap arg is given. |
|
560 |
Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... } |
|
558 |
@return: None if lvmap arg is given, otherwise, a dictionary |
|
559 |
of the form { 'nodename' : ['volume1', 'volume2', ...], ... } |
|
561 | 560 |
|
562 | 561 |
""" |
563 | 562 |
if node == None: |
b/lib/rapi/baserlib.py | ||
---|---|---|
32 | 32 |
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")): |
33 | 33 |
"""Builds a URI list as used by index resources. |
34 | 34 |
|
35 |
Args: |
|
36 |
- ids: List of ids as strings |
|
37 |
- uri_format: Format to be applied for URI |
|
38 |
- uri_fields: Optional parameter for field ids |
|
35 |
@param ids: list of ids as strings |
|
36 |
@param uri_format: format to be applied for URI |
|
37 |
@param uri_fields: optional parameter for field IDs |
|
39 | 38 |
|
40 | 39 |
""" |
41 | 40 |
(field_id, field_uri) = uri_fields |
... | ... | |
53 | 52 |
def ExtractField(sequence, index): |
54 | 53 |
"""Creates a list containing one column out of a list of lists. |
55 | 54 |
|
56 |
Args: |
|
57 |
- sequence: Sequence of lists |
|
58 |
- index: Index of field |
|
55 |
@param sequence: sequence of lists |
|
56 |
@param index: index of field |
|
59 | 57 |
|
60 | 58 |
""" |
61 | 59 |
return map(lambda item: item[index], sequence) |
... | ... | |
64 | 62 |
def MapFields(names, data): |
65 | 63 |
"""Maps two lists into one dictionary. |
66 | 64 |
|
67 |
Args:
|
|
68 |
- names: Field names (list of strings)
|
|
69 |
- data: Field data (list)
|
|
65 |
Example::
|
|
66 |
>>> MapFields(["a", "b"], ["foo", 123])
|
|
67 |
{'a': 'foo', 'b': 123}
|
|
70 | 68 |
|
71 |
Example: |
|
72 |
>>> MapFields(["a", "b"], ["foo", 123]) |
|
73 |
{'a': 'foo', 'b': 123} |
|
69 |
@param names: field names (list of strings) |
|
70 |
@param data: field data (list) |
|
74 | 71 |
|
75 | 72 |
""" |
76 | 73 |
if len(names) != len(data): |
... | ... | |
108 | 105 |
def MapBulkFields(itemslist, fields): |
109 | 106 |
"""Map value to field name in to one dictionary. |
110 | 107 |
|
111 |
Args: |
|
112 |
- itemslist: A list of items values |
|
113 |
- instance: A list of items names |
|
108 |
@param itemslist: a list of items values |
|
109 |
@param fields: a list of items names |
|
110 |
|
|
111 |
@return: a list of mapped dictionaries |
|
114 | 112 |
|
115 |
Returns: |
|
116 |
A list of mapped dictionaries |
|
117 | 113 |
""" |
118 | 114 |
items_details = [] |
119 | 115 |
for item in itemslist: |
... | ... | |
123 | 119 |
|
124 | 120 |
|
125 | 121 |
def MakeParamsDict(opts, params): |
126 |
""" Makes params dictionary out of a option set.
|
|
122 |
"""Makes params dictionary out of a option set. |
|
127 | 123 |
|
128 | 124 |
This function returns a dictionary needed for hv or be parameters. But only |
129 | 125 |
those fields which provided in the option set. Takes parameters frozensets |
... | ... | |
156 | 152 |
def __init__(self, items, queryargs, req): |
157 | 153 |
"""Generic resource constructor. |
158 | 154 |
|
159 |
Args: |
|
160 |
items: a list with variables encoded in the URL |
|
161 |
queryargs: a dictionary with additional options from URL |
|
155 |
@param items: a list with variables encoded in the URL |
|
156 |
@param queryargs: a dictionary with additional options from URL |
|
162 | 157 |
|
163 | 158 |
""" |
164 | 159 |
self.items = items |
b/lib/rapi/connector.py | ||
---|---|---|
43 | 43 |
def __init__(self, connector=CONNECTOR): |
44 | 44 |
"""Resource mapper constructor. |
45 | 45 |
|
46 |
Args: |
|
47 |
con: a dictionary, mapping method name with URL path regexp |
|
46 |
@param connector: a dictionary, mapping method name with URL path regexp |
|
48 | 47 |
|
49 | 48 |
""" |
50 | 49 |
self._connector = connector |
... | ... | |
52 | 51 |
def getController(self, uri): |
53 | 52 |
"""Find method for a given URI. |
54 | 53 |
|
55 |
Args: |
|
56 |
uri: string with URI |
|
54 |
@param uri: string with URI |
|
57 | 55 |
|
58 |
Returns:
|
|
59 |
None if no method is found or a tuple containing the following fields:
|
|
60 |
methd: name of method mapped to URI
|
|
61 |
items: a list of variable intems in the path |
|
62 |
args: a dictionary with additional parameters from URL |
|
56 |
@return: None if no method is found or a tuple containing
|
|
57 |
the following fields:
|
|
58 |
- method: name of method mapped to URI
|
|
59 |
- items: a list of variable intems in the path
|
|
60 |
- args: a dictionary with additional parameters from URL
|
|
63 | 61 |
|
64 | 62 |
""" |
65 | 63 |
if '?' in uri: |
... | ... | |
100 | 98 |
def GET(self): |
101 | 99 |
"""Show the list of mapped resources. |
102 | 100 |
|
103 |
Returns: |
|
104 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
101 |
@return: a dictionary with 'name' and 'uri' keys for each of them. |
|
105 | 102 |
|
106 | 103 |
""" |
107 | 104 |
root_pattern = re.compile('^R_([a-zA-Z0-9]+)$') |
b/lib/rapi/rlib1.py | ||
---|---|---|
45 | 45 |
class R_version(baserlib.R_Generic): |
46 | 46 |
"""/version resource. |
47 | 47 |
|
48 |
This resource should be used to determine the remote API version and to adapt
|
|
49 |
clients accordingly. |
|
48 |
This resource should be used to determine the remote API version and |
|
49 |
to adapt clients accordingly.
|
|
50 | 50 |
|
51 | 51 |
""" |
52 | 52 |
DOC_URI = "/version" |
... | ... | |
84 | 84 |
def GET(self): |
85 | 85 |
"""Returns cluster information. |
86 | 86 |
|
87 |
Example: { |
|
88 |
"config_version": 3, |
|
89 |
"name": "cluster1.example.com", |
|
90 |
"software_version": "1.2.4", |
|
91 |
"os_api_version": 5, |
|
92 |
"export_version": 0, |
|
93 |
"master": "node1.example.com", |
|
94 |
"architecture": [ |
|
95 |
"64bit", |
|
96 |
"x86_64" |
|
97 |
], |
|
98 |
"hypervisor_type": "xen-pvm", |
|
99 |
"protocol_version": 12 |
|
100 |
} |
|
87 |
Example:: |
|
88 |
|
|
89 |
{ |
|
90 |
"config_version": 3, |
|
91 |
"name": "cluster1.example.com", |
|
92 |
"software_version": "1.2.4", |
|
93 |
"os_api_version": 5, |
|
94 |
"export_version": 0, |
|
95 |
"master": "node1.example.com", |
|
96 |
"architecture": [ |
|
97 |
"64bit", |
|
98 |
"x86_64" |
|
99 |
], |
|
100 |
"hypervisor_type": "xen-pvm", |
|
101 |
"protocol_version": 12 |
|
102 |
} |
|
101 | 103 |
|
102 | 104 |
""" |
103 | 105 |
op = ganeti.opcodes.OpQueryClusterInfo() |
b/lib/rapi/rlib2.py | ||
---|---|---|
41 | 41 |
def GET(self): |
42 | 42 |
"""Returns a dictionary of jobs. |
43 | 43 |
|
44 |
Returns: |
|
45 |
A dictionary with jobs id and uri. |
|
44 |
@return: a dictionary with jobs id and uri. |
|
46 | 45 |
|
47 | 46 |
""" |
48 | 47 |
fields = ["id"] |
... | ... | |
60 | 59 |
def GET(self): |
61 | 60 |
"""Returns a job status. |
62 | 61 |
|
63 |
Returns: |
|
64 |
A dictionary with job parameters. |
|
65 |
|
|
66 |
The result includes: |
|
67 |
id - job ID as a number |
|
68 |
status - current job status as a string |
|
69 |
ops - involved OpCodes as a list of dictionaries for each opcodes in |
Also available in: Unified diff