Revision 067dda99 util/rapi.py
b/util/rapi.py | ||
---|---|---|
34 | 34 |
# be standalone. |
35 | 35 |
|
36 | 36 |
import logging |
37 |
import simplejson |
|
37 | 38 |
import socket |
38 | 39 |
import urllib |
39 | 40 |
import threading |
40 | 41 |
import pycurl |
41 | 42 |
|
42 | 43 |
try: |
43 |
import simplejson as json |
|
44 |
except ImportError: |
|
45 |
import json |
|
46 |
|
|
47 |
try: |
|
48 | 44 |
from cStringIO import StringIO |
49 | 45 |
except ImportError: |
50 | 46 |
from StringIO import StringIO |
... | ... | |
75 | 71 |
# Internal constants |
76 | 72 |
_REQ_DATA_VERSION_FIELD = "__version__" |
77 | 73 |
_INST_CREATE_REQV1 = "instance-create-reqv1" |
74 |
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1" |
|
78 | 75 |
_INST_NIC_PARAMS = frozenset(["mac", "ip", "mode", "link", "bridge"]) |
79 | 76 |
_INST_CREATE_V0_DISK_PARAMS = frozenset(["size"]) |
80 | 77 |
_INST_CREATE_V0_PARAMS = frozenset([ |
... | ... | |
239 | 236 |
return _ConfigCurl |
240 | 237 |
|
241 | 238 |
|
242 |
class GanetiRapiClient(object): |
|
239 |
class GanetiRapiClient(object): # pylint: disable-msg=R0904
|
|
243 | 240 |
"""Ganeti RAPI client. |
244 | 241 |
|
245 | 242 |
""" |
246 | 243 |
USER_AGENT = "Ganeti RAPI Client" |
247 |
_json_encoder = json.JSONEncoder(sort_keys=True) |
|
244 |
_json_encoder = simplejson.JSONEncoder(sort_keys=True)
|
|
248 | 245 |
|
249 | 246 |
def __init__(self, host, port=GANETI_RAPI_PORT, |
250 | 247 |
username=None, password=None, logger=logging, |
... | ... | |
421 | 418 |
|
422 | 419 |
# Was anything written to the response buffer? |
423 | 420 |
if encoded_resp_body.tell(): |
424 |
response_content = json.loads(encoded_resp_body.getvalue()) |
|
421 |
response_content = simplejson.loads(encoded_resp_body.getvalue())
|
|
425 | 422 |
else: |
426 | 423 |
response_content = None |
427 | 424 |
|
... | ... | |
484 | 481 |
return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION, |
485 | 482 |
None, None) |
486 | 483 |
|
484 |
def RedistributeConfig(self): |
|
485 |
"""Tells the cluster to redistribute its configuration files. |
|
486 |
|
|
487 |
@return: job id |
|
488 |
|
|
489 |
""" |
|
490 |
return self._SendRequest(HTTP_PUT, |
|
491 |
"/%s/redistribute-config" % GANETI_RAPI_VERSION, |
|
492 |
None, None) |
|
493 |
|
|
494 |
def ModifyCluster(self, **kwargs): |
|
495 |
"""Modifies cluster parameters. |
|
496 |
|
|
497 |
More details for parameters can be found in the RAPI documentation. |
|
498 |
|
|
499 |
@rtype: int |
|
500 |
@return: job id |
|
501 |
|
|
502 |
""" |
|
503 |
body = kwargs |
|
504 |
|
|
505 |
return self._SendRequest(HTTP_PUT, |
|
506 |
"/%s/modify" % GANETI_RAPI_VERSION, None, body) |
|
507 |
|
|
487 | 508 |
def GetClusterTags(self): |
488 | 509 |
"""Gets the cluster tags. |
489 | 510 |
|
... | ... | |
746 | 767 |
("/%s/instances/%s/modify" % |
747 | 768 |
(GANETI_RAPI_VERSION, instance)), None, body) |
748 | 769 |
|
770 |
def ActivateInstanceDisks(self, instance, ignore_size=None): |
|
771 |
"""Activates an instance's disks. |
|
772 |
|
|
773 |
@type instance: string |
|
774 |
@param instance: Instance name |
|
775 |
@type ignore_size: bool |
|
776 |
@param ignore_size: Whether to ignore recorded size |
|
777 |
@return: job id |
|
778 |
|
|
779 |
""" |
|
780 |
query = [] |
|
781 |
if ignore_size: |
|
782 |
query.append(("ignore_size", 1)) |
|
783 |
|
|
784 |
return self._SendRequest(HTTP_PUT, |
|
785 |
("/%s/instances/%s/activate-disks" % |
|
786 |
(GANETI_RAPI_VERSION, instance)), query, None) |
|
787 |
|
|
788 |
def DeactivateInstanceDisks(self, instance): |
|
789 |
"""Deactivates an instance's disks. |
|
790 |
|
|
791 |
@type instance: string |
|
792 |
@param instance: Instance name |
|
793 |
@return: job id |
|
794 |
|
|
795 |
""" |
|
796 |
return self._SendRequest(HTTP_PUT, |
|
797 |
("/%s/instances/%s/deactivate-disks" % |
|
798 |
(GANETI_RAPI_VERSION, instance)), None, None) |
|
799 |
|
|
800 |
def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None): |
|
801 |
"""Grows a disk of an instance. |
|
802 |
|
|
803 |
More details for parameters can be found in the RAPI documentation. |
|
804 |
|
|
805 |
@type instance: string |
|
806 |
@param instance: Instance name |
|
807 |
@type disk: integer |
|
808 |
@param disk: Disk index |
|
809 |
@type amount: integer |
|
810 |
@param amount: Grow disk by this amount (MiB) |
|
811 |
@type wait_for_sync: bool |
|
812 |
@param wait_for_sync: Wait for disk to synchronize |
|
813 |
@rtype: int |
|
814 |
@return: job id |
|
815 |
|
|
816 |
""" |
|
817 |
body = { |
|
818 |
"amount": amount, |
|
819 |
} |
|
820 |
|
|
821 |
if wait_for_sync is not None: |
|
822 |
body["wait_for_sync"] = wait_for_sync |
|
823 |
|
|
824 |
return self._SendRequest(HTTP_POST, |
|
825 |
("/%s/instances/%s/disk/%s/grow" % |
|
826 |
(GANETI_RAPI_VERSION, instance, disk)), |
|
827 |
None, body) |
|
828 |
|
|
749 | 829 |
def GetInstanceTags(self, instance): |
750 | 830 |
"""Gets tags for an instance. |
751 | 831 |
|
... | ... | |
862 | 942 |
("/%s/instances/%s/startup" % |
863 | 943 |
(GANETI_RAPI_VERSION, instance)), query, None) |
864 | 944 |
|
865 |
def ReinstallInstance(self, instance, os=None, no_startup=False): |
|
945 |
def ReinstallInstance(self, instance, os=None, no_startup=False, |
|
946 |
osparams=None): |
|
866 | 947 |
"""Reinstalls an instance. |
867 | 948 |
|
868 | 949 |
@type instance: str |
... | ... | |
874 | 955 |
@param no_startup: Whether to start the instance automatically |
875 | 956 |
|
876 | 957 |
""" |
958 |
if _INST_REINSTALL_REQV1 in self.GetFeatures(): |
|
959 |
body = { |
|
960 |
"start": not no_startup, |
|
961 |
} |
|
962 |
if os is not None: |
|
963 |
body["os"] = os |
|
964 |
if osparams is not None: |
|
965 |
body["osparams"] = osparams |
|
966 |
return self._SendRequest(HTTP_POST, |
|
967 |
("/%s/instances/%s/reinstall" % |
|
968 |
(GANETI_RAPI_VERSION, instance)), None, body) |
|
969 |
|
|
970 |
# Use old request format |
|
971 |
if osparams: |
|
972 |
raise GanetiApiError("Server does not support specifying OS parameters" |
|
973 |
" for instance reinstallation") |
|
974 |
|
|
877 | 975 |
query = [] |
878 | 976 |
if os: |
879 | 977 |
query.append(("os", os)) |
... | ... | |
1026 | 1124 |
("/%s/instances/%s/rename" % |
1027 | 1125 |
(GANETI_RAPI_VERSION, instance)), None, body) |
1028 | 1126 |
|
1127 |
def GetInstanceConsole(self, instance): |
|
1128 |
"""Request information for connecting to instance's console. |
|
1129 |
|
|
1130 |
@type instance: string |
|
1131 |
@param instance: Instance name |
|
1132 |
|
|
1133 |
""" |
|
1134 |
return self._SendRequest(HTTP_GET, |
|
1135 |
("/%s/instances/%s/console" % |
|
1136 |
(GANETI_RAPI_VERSION, instance)), None, None) |
|
1137 |
|
|
1029 | 1138 |
def GetJobs(self): |
1030 | 1139 |
"""Gets all jobs for the cluster. |
1031 | 1140 |
|
... | ... | |
1356 | 1465 |
return self._SendRequest(HTTP_DELETE, |
1357 | 1466 |
("/%s/nodes/%s/tags" % |
1358 | 1467 |
(GANETI_RAPI_VERSION, node)), query, None) |
1468 |
|
|
1469 |
def GetGroups(self, bulk=False): |
|
1470 |
"""Gets all node groups in the cluster. |
|
1471 |
|
|
1472 |
@type bulk: bool |
|
1473 |
@param bulk: whether to return all information about the groups |
|
1474 |
|
|
1475 |
@rtype: list of dict or str |
|
1476 |
@return: if bulk is true, a list of dictionaries with info about all node |
|
1477 |
groups in the cluster, else a list of names of those node groups |
|
1478 |
|
|
1479 |
""" |
|
1480 |
query = [] |
|
1481 |
if bulk: |
|
1482 |
query.append(("bulk", 1)) |
|
1483 |
|
|
1484 |
groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION, |
|
1485 |
query, None) |
|
1486 |
if bulk: |
|
1487 |
return groups |
|
1488 |
else: |
|
1489 |
return [g["name"] for g in groups] |
|
1490 |
|
|
1491 |
def GetGroup(self, group): |
|
1492 |
"""Gets information about a node group. |
|
1493 |
|
|
1494 |
@type group: str |
|
1495 |
@param group: name of the node group whose info to return |
|
1496 |
|
|
1497 |
@rtype: dict |
|
1498 |
@return: info about the node group |
|
1499 |
|
|
1500 |
""" |
|
1501 |
return self._SendRequest(HTTP_GET, |
|
1502 |
"/%s/groups/%s" % (GANETI_RAPI_VERSION, group), |
|
1503 |
None, None) |
|
1504 |
|
|
1505 |
def CreateGroup(self, name, alloc_policy=None, dry_run=False): |
|
1506 |
"""Creates a new node group. |
|
1507 |
|
|
1508 |
@type name: str |
|
1509 |
@param name: the name of node group to create |
|
1510 |
@type alloc_policy: str |
|
1511 |
@param alloc_policy: the desired allocation policy for the group, if any |
|
1512 |
@type dry_run: bool |
|
1513 |
@param dry_run: whether to peform a dry run |
|
1514 |
|
|
1515 |
@rtype: int |
|
1516 |
@return: job id |
|
1517 |
|
|
1518 |
""" |
|
1519 |
query = [] |
|
1520 |
if dry_run: |
|
1521 |
query.append(("dry-run", 1)) |
|
1522 |
|
|
1523 |
body = { |
|
1524 |
"name": name, |
|
1525 |
"alloc_policy": alloc_policy |
|
1526 |
} |
|
1527 |
|
|
1528 |
return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION, |
|
1529 |
query, body) |
|
1530 |
|
|
1531 |
def ModifyGroup(self, group, **kwargs): |
|
1532 |
"""Modifies a node group. |
|
1533 |
|
|
1534 |
More details for parameters can be found in the RAPI documentation. |
|
1535 |
|
|
1536 |
@type group: string |
|
1537 |
@param group: Node group name |
|
1538 |
@rtype: int |
|
1539 |
@return: job id |
|
1540 |
|
|
1541 |
""" |
|
1542 |
return self._SendRequest(HTTP_PUT, |
|
1543 |
("/%s/groups/%s/modify" % |
|
1544 |
(GANETI_RAPI_VERSION, group)), None, kwargs) |
|
1545 |
|
|
1546 |
def DeleteGroup(self, group, dry_run=False): |
|
1547 |
"""Deletes a node group. |
|
1548 |
|
|
1549 |
@type group: str |
|
1550 |
@param group: the node group to delete |
|
1551 |
@type dry_run: bool |
|
1552 |
@param dry_run: whether to peform a dry run |
|
1553 |
|
|
1554 |
@rtype: int |
|
1555 |
@return: job id |
|
1556 |
|
|
1557 |
""" |
|
1558 |
query = [] |
|
1559 |
if dry_run: |
|
1560 |
query.append(("dry-run", 1)) |
|
1561 |
|
|
1562 |
return self._SendRequest(HTTP_DELETE, |
|
1563 |
("/%s/groups/%s" % |
|
1564 |
(GANETI_RAPI_VERSION, group)), query, None) |
|
1565 |
|
|
1566 |
def RenameGroup(self, group, new_name): |
|
1567 |
"""Changes the name of a node group. |
|
1568 |
|
|
1569 |
@type group: string |
|
1570 |
@param group: Node group name |
|
1571 |
@type new_name: string |
|
1572 |
@param new_name: New node group name |
|
1573 |
|
|
1574 |
@rtype: int |
|
1575 |
@return: job id |
|
1576 |
|
|
1577 |
""" |
|
1578 |
body = { |
|
1579 |
"new_name": new_name, |
|
1580 |
} |
|
1581 |
|
|
1582 |
return self._SendRequest(HTTP_PUT, |
|
1583 |
("/%s/groups/%s/rename" % |
|
1584 |
(GANETI_RAPI_VERSION, group)), None, body) |
|
1585 |
|
|
1586 |
|
|
1587 |
def AssignGroupNodes(self, group, nodes, force=False, dry_run=False): |
|
1588 |
"""Assigns nodes to a group. |
|
1589 |
|
|
1590 |
@type group: string |
|
1591 |
@param group: Node gropu name |
|
1592 |
@type nodes: list of strings |
|
1593 |
@param nodes: List of nodes to assign to the group |
|
1594 |
|
|
1595 |
@rtype: int |
|
1596 |
@return: job id |
|
1597 |
|
|
1598 |
""" |
|
1599 |
query = [] |
|
1600 |
|
|
1601 |
if force: |
|
1602 |
query.append(("force", 1)) |
|
1603 |
|
|
1604 |
if dry_run: |
|
1605 |
query.append(("dry-run", 1)) |
|
1606 |
|
|
1607 |
body = { |
|
1608 |
"nodes": nodes, |
|
1609 |
} |
|
1610 |
|
|
1611 |
return self._SendRequest(HTTP_PUT, |
|
1612 |
("/%s/groups/%s/assign-nodes" % |
|
1613 |
(GANETI_RAPI_VERSION, group)), query, body) |
Also available in: Unified diff