Statistics
| Branch: | Tag: | Revision:

root / util / rapi.py @ 5039a44f

History | View | Annotate | Download (46.5 kB)

1 e5e20779 Faidon Liambotis
#
2 e5e20779 Faidon Liambotis
#
3 e5e20779 Faidon Liambotis
4 e5e20779 Faidon Liambotis
# Copyright (C) 2010 Google Inc.
5 e5e20779 Faidon Liambotis
#
6 e5e20779 Faidon Liambotis
# This program is free software; you can redistribute it and/or modify
7 e5e20779 Faidon Liambotis
# it under the terms of the GNU General Public License as published by
8 e5e20779 Faidon Liambotis
# the Free Software Foundation; either version 2 of the License, or
9 e5e20779 Faidon Liambotis
# (at your option) any later version.
10 e5e20779 Faidon Liambotis
#
11 e5e20779 Faidon Liambotis
# This program is distributed in the hope that it will be useful, but
12 e5e20779 Faidon Liambotis
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 e5e20779 Faidon Liambotis
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 e5e20779 Faidon Liambotis
# General Public License for more details.
15 e5e20779 Faidon Liambotis
#
16 e5e20779 Faidon Liambotis
# You should have received a copy of the GNU General Public License
17 e5e20779 Faidon Liambotis
# along with this program; if not, write to the Free Software
18 e5e20779 Faidon Liambotis
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 e5e20779 Faidon Liambotis
# 02110-1301, USA.
20 e5e20779 Faidon Liambotis
21 e5e20779 Faidon Liambotis
22 e5e20779 Faidon Liambotis
"""Ganeti RAPI client.
23 e5e20779 Faidon Liambotis

24 e5e20779 Faidon Liambotis
@attention: To use the RAPI client, the application B{must} call
25 e5e20779 Faidon Liambotis
            C{pycurl.global_init} during initialization and
26 e5e20779 Faidon Liambotis
            C{pycurl.global_cleanup} before exiting the process. This is very
27 e5e20779 Faidon Liambotis
            important in multi-threaded programs. See curl_global_init(3) and
28 e5e20779 Faidon Liambotis
            curl_global_cleanup(3) for details. The decorator L{UsesRapiClient}
29 e5e20779 Faidon Liambotis
            can be used.
30 e5e20779 Faidon Liambotis

31 e5e20779 Faidon Liambotis
"""
32 e5e20779 Faidon Liambotis
33 e5e20779 Faidon Liambotis
# No Ganeti-specific modules should be imported. The RAPI client is supposed to
34 e5e20779 Faidon Liambotis
# be standalone.
35 e5e20779 Faidon Liambotis
36 ee66adf0 Georgios Gousios
from synnefo.logic import log
37 ee66adf0 Georgios Gousios
38 e5e20779 Faidon Liambotis
import socket
39 e5e20779 Faidon Liambotis
import urllib
40 e5e20779 Faidon Liambotis
import threading
41 e5e20779 Faidon Liambotis
import pycurl
42 e5e20779 Faidon Liambotis
43 e5e20779 Faidon Liambotis
try:
44 3481786b Faidon Liambotis
  import simplejson as json
45 3481786b Faidon Liambotis
except ImportError:
46 3481786b Faidon Liambotis
  import json
47 3481786b Faidon Liambotis
48 3481786b Faidon Liambotis
try:
49 e5e20779 Faidon Liambotis
  from cStringIO import StringIO
50 e5e20779 Faidon Liambotis
except ImportError:
51 e5e20779 Faidon Liambotis
  from StringIO import StringIO
52 e5e20779 Faidon Liambotis
53 e5e20779 Faidon Liambotis
54 e5e20779 Faidon Liambotis
GANETI_RAPI_PORT = 5080
55 e5e20779 Faidon Liambotis
GANETI_RAPI_VERSION = 2
56 e5e20779 Faidon Liambotis
57 e5e20779 Faidon Liambotis
HTTP_DELETE = "DELETE"
58 e5e20779 Faidon Liambotis
HTTP_GET = "GET"
59 e5e20779 Faidon Liambotis
HTTP_PUT = "PUT"
60 e5e20779 Faidon Liambotis
HTTP_POST = "POST"
61 e5e20779 Faidon Liambotis
HTTP_OK = 200
62 e5e20779 Faidon Liambotis
HTTP_NOT_FOUND = 404
63 e5e20779 Faidon Liambotis
HTTP_APP_JSON = "application/json"
64 e5e20779 Faidon Liambotis
65 e5e20779 Faidon Liambotis
REPLACE_DISK_PRI = "replace_on_primary"
66 e5e20779 Faidon Liambotis
REPLACE_DISK_SECONDARY = "replace_on_secondary"
67 e5e20779 Faidon Liambotis
REPLACE_DISK_CHG = "replace_new_secondary"
68 e5e20779 Faidon Liambotis
REPLACE_DISK_AUTO = "replace_auto"
69 e5e20779 Faidon Liambotis
70 e5e20779 Faidon Liambotis
NODE_ROLE_DRAINED = "drained"
71 e5e20779 Faidon Liambotis
NODE_ROLE_MASTER_CANDIATE = "master-candidate"
72 e5e20779 Faidon Liambotis
NODE_ROLE_MASTER = "master"
73 e5e20779 Faidon Liambotis
NODE_ROLE_OFFLINE = "offline"
74 e5e20779 Faidon Liambotis
NODE_ROLE_REGULAR = "regular"
75 e5e20779 Faidon Liambotis
76 e5e20779 Faidon Liambotis
# Internal constants
77 e5e20779 Faidon Liambotis
_REQ_DATA_VERSION_FIELD = "__version__"
78 e5e20779 Faidon Liambotis
_INST_CREATE_REQV1 = "instance-create-reqv1"
79 067dda99 Vangelis Koukis
_INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
80 e5e20779 Faidon Liambotis
_INST_NIC_PARAMS = frozenset(["mac", "ip", "mode", "link", "bridge"])
81 e5e20779 Faidon Liambotis
_INST_CREATE_V0_DISK_PARAMS = frozenset(["size"])
82 e5e20779 Faidon Liambotis
_INST_CREATE_V0_PARAMS = frozenset([
83 e5e20779 Faidon Liambotis
  "os", "pnode", "snode", "iallocator", "start", "ip_check", "name_check",
84 e5e20779 Faidon Liambotis
  "hypervisor", "file_storage_dir", "file_driver", "dry_run",
85 e5e20779 Faidon Liambotis
  ])
86 e5e20779 Faidon Liambotis
_INST_CREATE_V0_DPARAMS = frozenset(["beparams", "hvparams"])
87 e5e20779 Faidon Liambotis
88 e5e20779 Faidon Liambotis
# Older pycURL versions don't have all error constants
89 e5e20779 Faidon Liambotis
try:
90 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
91 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
92 e5e20779 Faidon Liambotis
except AttributeError:
93 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT = 60
94 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE = 77
95 e5e20779 Faidon Liambotis
96 e5e20779 Faidon Liambotis
_CURL_SSL_CERT_ERRORS = frozenset([
97 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT,
98 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE,
99 e5e20779 Faidon Liambotis
  ])
100 e5e20779 Faidon Liambotis
101 e5e20779 Faidon Liambotis
102 e5e20779 Faidon Liambotis
class Error(Exception):
103 e5e20779 Faidon Liambotis
  """Base error class for this module.
104 e5e20779 Faidon Liambotis

105 e5e20779 Faidon Liambotis
  """
106 e5e20779 Faidon Liambotis
  pass
107 e5e20779 Faidon Liambotis
108 e5e20779 Faidon Liambotis
109 e5e20779 Faidon Liambotis
class CertificateError(Error):
110 e5e20779 Faidon Liambotis
  """Raised when a problem is found with the SSL certificate.
111 e5e20779 Faidon Liambotis

112 e5e20779 Faidon Liambotis
  """
113 e5e20779 Faidon Liambotis
  pass
114 e5e20779 Faidon Liambotis
115 e5e20779 Faidon Liambotis
116 e5e20779 Faidon Liambotis
class GanetiApiError(Error):
117 e5e20779 Faidon Liambotis
  """Generic error raised from Ganeti API.
118 e5e20779 Faidon Liambotis

119 e5e20779 Faidon Liambotis
  """
120 e5e20779 Faidon Liambotis
  def __init__(self, msg, code=None):
121 e5e20779 Faidon Liambotis
    Error.__init__(self, msg)
122 e5e20779 Faidon Liambotis
    self.code = code
123 e5e20779 Faidon Liambotis
124 e5e20779 Faidon Liambotis
125 e5e20779 Faidon Liambotis
def UsesRapiClient(fn):
126 e5e20779 Faidon Liambotis
  """Decorator for code using RAPI client to initialize pycURL.
127 e5e20779 Faidon Liambotis

128 e5e20779 Faidon Liambotis
  """
129 e5e20779 Faidon Liambotis
  def wrapper(*args, **kwargs):
130 e5e20779 Faidon Liambotis
    # curl_global_init(3) and curl_global_cleanup(3) must be called with only
131 e5e20779 Faidon Liambotis
    # one thread running. This check is just a safety measure -- it doesn't
132 e5e20779 Faidon Liambotis
    # cover all cases.
133 e5e20779 Faidon Liambotis
    assert threading.activeCount() == 1, \
134 e5e20779 Faidon Liambotis
           "Found active threads when initializing pycURL"
135 e5e20779 Faidon Liambotis
136 e5e20779 Faidon Liambotis
    pycurl.global_init(pycurl.GLOBAL_ALL)
137 e5e20779 Faidon Liambotis
    try:
138 e5e20779 Faidon Liambotis
      return fn(*args, **kwargs)
139 e5e20779 Faidon Liambotis
    finally:
140 e5e20779 Faidon Liambotis
      pycurl.global_cleanup()
141 e5e20779 Faidon Liambotis
142 e5e20779 Faidon Liambotis
  return wrapper
143 e5e20779 Faidon Liambotis
144 e5e20779 Faidon Liambotis
145 e5e20779 Faidon Liambotis
def GenericCurlConfig(verbose=False, use_signal=False,
146 e5e20779 Faidon Liambotis
                      use_curl_cabundle=False, cafile=None, capath=None,
147 e5e20779 Faidon Liambotis
                      proxy=None, verify_hostname=False,
148 e5e20779 Faidon Liambotis
                      connect_timeout=None, timeout=None,
149 e5e20779 Faidon Liambotis
                      _pycurl_version_fn=pycurl.version_info):
150 e5e20779 Faidon Liambotis
  """Curl configuration function generator.
151 e5e20779 Faidon Liambotis

152 e5e20779 Faidon Liambotis
  @type verbose: bool
153 e5e20779 Faidon Liambotis
  @param verbose: Whether to set cURL to verbose mode
154 e5e20779 Faidon Liambotis
  @type use_signal: bool
155 e5e20779 Faidon Liambotis
  @param use_signal: Whether to allow cURL to use signals
156 e5e20779 Faidon Liambotis
  @type use_curl_cabundle: bool
157 e5e20779 Faidon Liambotis
  @param use_curl_cabundle: Whether to use cURL's default CA bundle
158 e5e20779 Faidon Liambotis
  @type cafile: string
159 e5e20779 Faidon Liambotis
  @param cafile: In which file we can find the certificates
160 e5e20779 Faidon Liambotis
  @type capath: string
161 e5e20779 Faidon Liambotis
  @param capath: In which directory we can find the certificates
162 e5e20779 Faidon Liambotis
  @type proxy: string
163 e5e20779 Faidon Liambotis
  @param proxy: Proxy to use, None for default behaviour and empty string for
164 e5e20779 Faidon Liambotis
                disabling proxies (see curl_easy_setopt(3))
165 e5e20779 Faidon Liambotis
  @type verify_hostname: bool
166 e5e20779 Faidon Liambotis
  @param verify_hostname: Whether to verify the remote peer certificate's
167 e5e20779 Faidon Liambotis
                          commonName
168 e5e20779 Faidon Liambotis
  @type connect_timeout: number
169 e5e20779 Faidon Liambotis
  @param connect_timeout: Timeout for establishing connection in seconds
170 e5e20779 Faidon Liambotis
  @type timeout: number
171 e5e20779 Faidon Liambotis
  @param timeout: Timeout for complete transfer in seconds (see
172 e5e20779 Faidon Liambotis
                  curl_easy_setopt(3)).
173 e5e20779 Faidon Liambotis

174 e5e20779 Faidon Liambotis
  """
175 e5e20779 Faidon Liambotis
  if use_curl_cabundle and (cafile or capath):
176 e5e20779 Faidon Liambotis
    raise Error("Can not use default CA bundle when CA file or path is set")
177 e5e20779 Faidon Liambotis
178 e5e20779 Faidon Liambotis
  def _ConfigCurl(curl, logger):
179 e5e20779 Faidon Liambotis
    """Configures a cURL object
180 e5e20779 Faidon Liambotis

181 e5e20779 Faidon Liambotis
    @type curl: pycurl.Curl
182 e5e20779 Faidon Liambotis
    @param curl: cURL object
183 e5e20779 Faidon Liambotis

184 e5e20779 Faidon Liambotis
    """
185 e5e20779 Faidon Liambotis
    logger.debug("Using cURL version %s", pycurl.version)
186 e5e20779 Faidon Liambotis
187 e5e20779 Faidon Liambotis
    # pycurl.version_info returns a tuple with information about the used
188 e5e20779 Faidon Liambotis
    # version of libcurl. Item 5 is the SSL library linked to it.
189 e5e20779 Faidon Liambotis
    # e.g.: (3, '7.18.0', 463360, 'x86_64-pc-linux-gnu', 1581, 'GnuTLS/2.0.4',
190 e5e20779 Faidon Liambotis
    # 0, '1.2.3.3', ...)
191 e5e20779 Faidon Liambotis
    sslver = _pycurl_version_fn()[5]
192 e5e20779 Faidon Liambotis
    if not sslver:
193 e5e20779 Faidon Liambotis
      raise Error("No SSL support in cURL")
194 e5e20779 Faidon Liambotis
195 e5e20779 Faidon Liambotis
    lcsslver = sslver.lower()
196 e5e20779 Faidon Liambotis
    if lcsslver.startswith("openssl/"):
197 e5e20779 Faidon Liambotis
      pass
198 e5e20779 Faidon Liambotis
    elif lcsslver.startswith("gnutls/"):
199 e5e20779 Faidon Liambotis
      if capath:
200 e5e20779 Faidon Liambotis
        raise Error("cURL linked against GnuTLS has no support for a"
201 e5e20779 Faidon Liambotis
                    " CA path (%s)" % (pycurl.version, ))
202 e5e20779 Faidon Liambotis
    else:
203 e5e20779 Faidon Liambotis
      raise NotImplementedError("cURL uses unsupported SSL version '%s'" %
204 e5e20779 Faidon Liambotis
                                sslver)
205 e5e20779 Faidon Liambotis
206 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.VERBOSE, verbose)
207 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.NOSIGNAL, not use_signal)
208 e5e20779 Faidon Liambotis
209 e5e20779 Faidon Liambotis
    # Whether to verify remote peer's CN
210 e5e20779 Faidon Liambotis
    if verify_hostname:
211 e5e20779 Faidon Liambotis
      # curl_easy_setopt(3): "When CURLOPT_SSL_VERIFYHOST is 2, that
212 e5e20779 Faidon Liambotis
      # certificate must indicate that the server is the server to which you
213 e5e20779 Faidon Liambotis
      # meant to connect, or the connection fails. [...] When the value is 1,
214 e5e20779 Faidon Liambotis
      # the certificate must contain a Common Name field, but it doesn't matter
215 e5e20779 Faidon Liambotis
      # what name it says. [...]"
216 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.SSL_VERIFYHOST, 2)
217 e5e20779 Faidon Liambotis
    else:
218 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.SSL_VERIFYHOST, 0)
219 e5e20779 Faidon Liambotis
220 e5e20779 Faidon Liambotis
    if cafile or capath or use_curl_cabundle:
221 e5e20779 Faidon Liambotis
      # Require certificates to be checked
222 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.SSL_VERIFYPEER, True)
223 e5e20779 Faidon Liambotis
      if cafile:
224 e5e20779 Faidon Liambotis
        curl.setopt(pycurl.CAINFO, str(cafile))
225 e5e20779 Faidon Liambotis
      if capath:
226 e5e20779 Faidon Liambotis
        curl.setopt(pycurl.CAPATH, str(capath))
227 e5e20779 Faidon Liambotis
      # Not changing anything for using default CA bundle
228 e5e20779 Faidon Liambotis
    else:
229 e5e20779 Faidon Liambotis
      # Disable SSL certificate verification
230 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.SSL_VERIFYPEER, False)
231 e5e20779 Faidon Liambotis
232 e5e20779 Faidon Liambotis
    if proxy is not None:
233 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.PROXY, str(proxy))
234 e5e20779 Faidon Liambotis
235 e5e20779 Faidon Liambotis
    # Timeouts
236 e5e20779 Faidon Liambotis
    if connect_timeout is not None:
237 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.CONNECTTIMEOUT, connect_timeout)
238 e5e20779 Faidon Liambotis
    if timeout is not None:
239 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.TIMEOUT, timeout)
240 e5e20779 Faidon Liambotis
241 e5e20779 Faidon Liambotis
  return _ConfigCurl
242 e5e20779 Faidon Liambotis
243 e5e20779 Faidon Liambotis
244 067dda99 Vangelis Koukis
class GanetiRapiClient(object): # pylint: disable-msg=R0904
245 e5e20779 Faidon Liambotis
  """Ganeti RAPI client.
246 e5e20779 Faidon Liambotis

247 e5e20779 Faidon Liambotis
  """
248 e5e20779 Faidon Liambotis
  USER_AGENT = "Ganeti RAPI Client"
249 1f6ba360 Faidon Liambotis
  _json_encoder = json.JSONEncoder(sort_keys=True)
250 e5e20779 Faidon Liambotis
251 e5e20779 Faidon Liambotis
  def __init__(self, host, port=GANETI_RAPI_PORT,
252 98f36397 Georgios Gousios
               username=None, password=None,
253 ee66adf0 Georgios Gousios
               logger=log.get_logger("rapi"),
254 e5e20779 Faidon Liambotis
               curl_config_fn=None, curl_factory=None):
255 e5e20779 Faidon Liambotis
    """Initializes this class.
256 e5e20779 Faidon Liambotis

257 e5e20779 Faidon Liambotis
    @type host: string
258 e5e20779 Faidon Liambotis
    @param host: the ganeti cluster master to interact with
259 e5e20779 Faidon Liambotis
    @type port: int
260 e5e20779 Faidon Liambotis
    @param port: the port on which the RAPI is running (default is 5080)
261 e5e20779 Faidon Liambotis
    @type username: string
262 e5e20779 Faidon Liambotis
    @param username: the username to connect with
263 e5e20779 Faidon Liambotis
    @type password: string
264 e5e20779 Faidon Liambotis
    @param password: the password to connect with
265 e5e20779 Faidon Liambotis
    @type curl_config_fn: callable
266 e5e20779 Faidon Liambotis
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
267 e5e20779 Faidon Liambotis
    @param logger: Logging object
268 e5e20779 Faidon Liambotis

269 e5e20779 Faidon Liambotis
    """
270 e5e20779 Faidon Liambotis
    self._username = username
271 e5e20779 Faidon Liambotis
    self._password = password
272 e5e20779 Faidon Liambotis
    self._logger = logger
273 e5e20779 Faidon Liambotis
    self._curl_config_fn = curl_config_fn
274 e5e20779 Faidon Liambotis
    self._curl_factory = curl_factory
275 e5e20779 Faidon Liambotis
276 e5e20779 Faidon Liambotis
    try:
277 e5e20779 Faidon Liambotis
      socket.inet_pton(socket.AF_INET6, host)
278 e5e20779 Faidon Liambotis
      address = "[%s]:%s" % (host, port)
279 e5e20779 Faidon Liambotis
    except socket.error:
280 e5e20779 Faidon Liambotis
      address = "%s:%s" % (host, port)
281 e5e20779 Faidon Liambotis
282 e5e20779 Faidon Liambotis
    self._base_url = "https://%s" % address
283 e5e20779 Faidon Liambotis
284 e5e20779 Faidon Liambotis
    if username is not None:
285 e5e20779 Faidon Liambotis
      if password is None:
286 e5e20779 Faidon Liambotis
        raise Error("Password not specified")
287 e5e20779 Faidon Liambotis
    elif password:
288 e5e20779 Faidon Liambotis
      raise Error("Specified password without username")
289 e5e20779 Faidon Liambotis
290 e5e20779 Faidon Liambotis
  def _CreateCurl(self):
291 e5e20779 Faidon Liambotis
    """Creates a cURL object.
292 e5e20779 Faidon Liambotis

293 e5e20779 Faidon Liambotis
    """
294 e5e20779 Faidon Liambotis
    # Create pycURL object if no factory is provided
295 e5e20779 Faidon Liambotis
    if self._curl_factory:
296 e5e20779 Faidon Liambotis
      curl = self._curl_factory()
297 e5e20779 Faidon Liambotis
    else:
298 e5e20779 Faidon Liambotis
      curl = pycurl.Curl()
299 e5e20779 Faidon Liambotis
300 e5e20779 Faidon Liambotis
    # Default cURL settings
301 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.VERBOSE, False)
302 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.FOLLOWLOCATION, False)
303 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.MAXREDIRS, 5)
304 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.NOSIGNAL, True)
305 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.USERAGENT, self.USER_AGENT)
306 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.SSL_VERIFYHOST, 0)
307 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.SSL_VERIFYPEER, False)
308 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.HTTPHEADER, [
309 e5e20779 Faidon Liambotis
      "Accept: %s" % HTTP_APP_JSON,
310 e5e20779 Faidon Liambotis
      "Content-type: %s" % HTTP_APP_JSON,
311 e5e20779 Faidon Liambotis
      ])
312 e5e20779 Faidon Liambotis
313 e5e20779 Faidon Liambotis
    assert ((self._username is None and self._password is None) ^
314 e5e20779 Faidon Liambotis
            (self._username is not None and self._password is not None))
315 e5e20779 Faidon Liambotis
316 e5e20779 Faidon Liambotis
    if self._username:
317 e5e20779 Faidon Liambotis
      # Setup authentication
318 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
319 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.USERPWD,
320 e5e20779 Faidon Liambotis
                  str("%s:%s" % (self._username, self._password)))
321 e5e20779 Faidon Liambotis
322 e5e20779 Faidon Liambotis
    # Call external configuration function
323 e5e20779 Faidon Liambotis
    if self._curl_config_fn:
324 e5e20779 Faidon Liambotis
      self._curl_config_fn(curl, self._logger)
325 e5e20779 Faidon Liambotis
326 e5e20779 Faidon Liambotis
    return curl
327 e5e20779 Faidon Liambotis
328 e5e20779 Faidon Liambotis
  @staticmethod
329 e5e20779 Faidon Liambotis
  def _EncodeQuery(query):
330 e5e20779 Faidon Liambotis
    """Encode query values for RAPI URL.
331 e5e20779 Faidon Liambotis

332 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
333 e5e20779 Faidon Liambotis
    @param query: Query arguments
334 e5e20779 Faidon Liambotis
    @rtype: list
335 e5e20779 Faidon Liambotis
    @return: Query list with encoded values
336 e5e20779 Faidon Liambotis

337 e5e20779 Faidon Liambotis
    """
338 e5e20779 Faidon Liambotis
    result = []
339 e5e20779 Faidon Liambotis
340 e5e20779 Faidon Liambotis
    for name, value in query:
341 e5e20779 Faidon Liambotis
      if value is None:
342 e5e20779 Faidon Liambotis
        result.append((name, ""))
343 e5e20779 Faidon Liambotis
344 e5e20779 Faidon Liambotis
      elif isinstance(value, bool):
345 e5e20779 Faidon Liambotis
        # Boolean values must be encoded as 0 or 1
346 e5e20779 Faidon Liambotis
        result.append((name, int(value)))
347 e5e20779 Faidon Liambotis
348 e5e20779 Faidon Liambotis
      elif isinstance(value, (list, tuple, dict)):
349 e5e20779 Faidon Liambotis
        raise ValueError("Invalid query data type %r" % type(value).__name__)
350 e5e20779 Faidon Liambotis
351 e5e20779 Faidon Liambotis
      else:
352 e5e20779 Faidon Liambotis
        result.append((name, value))
353 e5e20779 Faidon Liambotis
354 e5e20779 Faidon Liambotis
    return result
355 e5e20779 Faidon Liambotis
356 e5e20779 Faidon Liambotis
  def _SendRequest(self, method, path, query, content):
357 e5e20779 Faidon Liambotis
    """Sends an HTTP request.
358 e5e20779 Faidon Liambotis

359 e5e20779 Faidon Liambotis
    This constructs a full URL, encodes and decodes HTTP bodies, and
360 e5e20779 Faidon Liambotis
    handles invalid responses in a pythonic way.
361 e5e20779 Faidon Liambotis

362 e5e20779 Faidon Liambotis
    @type method: string
363 e5e20779 Faidon Liambotis
    @param method: HTTP method to use
364 e5e20779 Faidon Liambotis
    @type path: string
365 e5e20779 Faidon Liambotis
    @param path: HTTP URL path
366 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
367 e5e20779 Faidon Liambotis
    @param query: query arguments to pass to urllib.urlencode
368 e5e20779 Faidon Liambotis
    @type content: str or None
369 e5e20779 Faidon Liambotis
    @param content: HTTP body content
370 e5e20779 Faidon Liambotis

371 e5e20779 Faidon Liambotis
    @rtype: str
372 e5e20779 Faidon Liambotis
    @return: JSON-Decoded response
373 e5e20779 Faidon Liambotis

374 e5e20779 Faidon Liambotis
    @raises CertificateError: If an invalid SSL certificate is found
375 e5e20779 Faidon Liambotis
    @raises GanetiApiError: If an invalid response is returned
376 e5e20779 Faidon Liambotis

377 e5e20779 Faidon Liambotis
    """
378 e5e20779 Faidon Liambotis
    assert path.startswith("/")
379 e5e20779 Faidon Liambotis
380 e5e20779 Faidon Liambotis
    curl = self._CreateCurl()
381 e5e20779 Faidon Liambotis
382 e5e20779 Faidon Liambotis
    if content is not None:
383 e5e20779 Faidon Liambotis
      encoded_content = self._json_encoder.encode(content)
384 e5e20779 Faidon Liambotis
    else:
385 e5e20779 Faidon Liambotis
      encoded_content = ""
386 e5e20779 Faidon Liambotis
387 e5e20779 Faidon Liambotis
    # Build URL
388 e5e20779 Faidon Liambotis
    urlparts = [self._base_url, path]
389 e5e20779 Faidon Liambotis
    if query:
390 e5e20779 Faidon Liambotis
      urlparts.append("?")
391 e5e20779 Faidon Liambotis
      urlparts.append(urllib.urlencode(self._EncodeQuery(query)))
392 e5e20779 Faidon Liambotis
393 e5e20779 Faidon Liambotis
    url = "".join(urlparts)
394 e5e20779 Faidon Liambotis
395 e5e20779 Faidon Liambotis
    self._logger.debug("Sending request %s %s (content=%r)",
396 e5e20779 Faidon Liambotis
                       method, url, encoded_content)
397 e5e20779 Faidon Liambotis
398 e5e20779 Faidon Liambotis
    # Buffer for response
399 e5e20779 Faidon Liambotis
    encoded_resp_body = StringIO()
400 e5e20779 Faidon Liambotis
401 e5e20779 Faidon Liambotis
    # Configure cURL
402 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.CUSTOMREQUEST, str(method))
403 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.URL, str(url))
404 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.POSTFIELDS, str(encoded_content))
405 e5e20779 Faidon Liambotis
    curl.setopt(pycurl.WRITEFUNCTION, encoded_resp_body.write)
406 e5e20779 Faidon Liambotis
407 e5e20779 Faidon Liambotis
    try:
408 e5e20779 Faidon Liambotis
      # Send request and wait for response
409 e5e20779 Faidon Liambotis
      try:
410 e5e20779 Faidon Liambotis
        curl.perform()
411 e5e20779 Faidon Liambotis
      except pycurl.error, err:
412 e5e20779 Faidon Liambotis
        if err.args[0] in _CURL_SSL_CERT_ERRORS:
413 e5e20779 Faidon Liambotis
          raise CertificateError("SSL certificate error %s" % err)
414 e5e20779 Faidon Liambotis
415 e5e20779 Faidon Liambotis
        raise GanetiApiError(str(err))
416 e5e20779 Faidon Liambotis
    finally:
417 e5e20779 Faidon Liambotis
      # Reset settings to not keep references to large objects in memory
418 e5e20779 Faidon Liambotis
      # between requests
419 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.POSTFIELDS, "")
420 e5e20779 Faidon Liambotis
      curl.setopt(pycurl.WRITEFUNCTION, lambda _: None)
421 e5e20779 Faidon Liambotis
422 e5e20779 Faidon Liambotis
    # Get HTTP response code
423 e5e20779 Faidon Liambotis
    http_code = curl.getinfo(pycurl.RESPONSE_CODE)
424 e5e20779 Faidon Liambotis
425 e5e20779 Faidon Liambotis
    # Was anything written to the response buffer?
426 e5e20779 Faidon Liambotis
    if encoded_resp_body.tell():
427 1f6ba360 Faidon Liambotis
      response_content = json.loads(encoded_resp_body.getvalue())
428 e5e20779 Faidon Liambotis
    else:
429 e5e20779 Faidon Liambotis
      response_content = None
430 e5e20779 Faidon Liambotis
431 e5e20779 Faidon Liambotis
    if http_code != HTTP_OK:
432 e5e20779 Faidon Liambotis
      if isinstance(response_content, dict):
433 e5e20779 Faidon Liambotis
        msg = ("%s %s: %s" %
434 e5e20779 Faidon Liambotis
               (response_content["code"],
435 e5e20779 Faidon Liambotis
                response_content["message"],
436 e5e20779 Faidon Liambotis
                response_content["explain"]))
437 e5e20779 Faidon Liambotis
      else:
438 e5e20779 Faidon Liambotis
        msg = str(response_content)
439 e5e20779 Faidon Liambotis
440 e5e20779 Faidon Liambotis
      raise GanetiApiError(msg, code=http_code)
441 e5e20779 Faidon Liambotis
442 e5e20779 Faidon Liambotis
    return response_content
443 e5e20779 Faidon Liambotis
444 e5e20779 Faidon Liambotis
  def GetVersion(self):
445 e5e20779 Faidon Liambotis
    """Gets the Remote API version running on the cluster.
446 e5e20779 Faidon Liambotis

447 e5e20779 Faidon Liambotis
    @rtype: int
448 e5e20779 Faidon Liambotis
    @return: Ganeti Remote API version
449 e5e20779 Faidon Liambotis

450 e5e20779 Faidon Liambotis
    """
451 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/version", None, None)
452 e5e20779 Faidon Liambotis
453 e5e20779 Faidon Liambotis
  def GetFeatures(self):
454 e5e20779 Faidon Liambotis
    """Gets the list of optional features supported by RAPI server.
455 e5e20779 Faidon Liambotis

456 e5e20779 Faidon Liambotis
    @rtype: list
457 e5e20779 Faidon Liambotis
    @return: List of optional features
458 e5e20779 Faidon Liambotis

459 e5e20779 Faidon Liambotis
    """
460 e5e20779 Faidon Liambotis
    try:
461 e5e20779 Faidon Liambotis
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
462 e5e20779 Faidon Liambotis
                               None, None)
463 e5e20779 Faidon Liambotis
    except GanetiApiError, err:
464 e5e20779 Faidon Liambotis
      # Older RAPI servers don't support this resource
465 e5e20779 Faidon Liambotis
      if err.code == HTTP_NOT_FOUND:
466 e5e20779 Faidon Liambotis
        return []
467 e5e20779 Faidon Liambotis
468 e5e20779 Faidon Liambotis
      raise
469 e5e20779 Faidon Liambotis
470 e5e20779 Faidon Liambotis
  def GetOperatingSystems(self):
471 e5e20779 Faidon Liambotis
    """Gets the Operating Systems running in the Ganeti cluster.
472 e5e20779 Faidon Liambotis

473 e5e20779 Faidon Liambotis
    @rtype: list of str
474 e5e20779 Faidon Liambotis
    @return: operating systems
475 e5e20779 Faidon Liambotis

476 e5e20779 Faidon Liambotis
    """
477 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
478 e5e20779 Faidon Liambotis
                             None, None)
479 e5e20779 Faidon Liambotis
480 e5e20779 Faidon Liambotis
  def GetInfo(self):
481 e5e20779 Faidon Liambotis
    """Gets info about the cluster.
482 e5e20779 Faidon Liambotis

483 e5e20779 Faidon Liambotis
    @rtype: dict
484 e5e20779 Faidon Liambotis
    @return: information about the cluster
485 e5e20779 Faidon Liambotis

486 e5e20779 Faidon Liambotis
    """
487 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
488 e5e20779 Faidon Liambotis
                             None, None)
489 e5e20779 Faidon Liambotis
490 067dda99 Vangelis Koukis
  def RedistributeConfig(self):
491 067dda99 Vangelis Koukis
    """Tells the cluster to redistribute its configuration files.
492 067dda99 Vangelis Koukis

493 067dda99 Vangelis Koukis
    @return: job id
494 067dda99 Vangelis Koukis

495 067dda99 Vangelis Koukis
    """
496 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
497 067dda99 Vangelis Koukis
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
498 067dda99 Vangelis Koukis
                             None, None)
499 067dda99 Vangelis Koukis
500 067dda99 Vangelis Koukis
  def ModifyCluster(self, **kwargs):
501 067dda99 Vangelis Koukis
    """Modifies cluster parameters.
502 067dda99 Vangelis Koukis

503 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
504 067dda99 Vangelis Koukis

505 067dda99 Vangelis Koukis
    @rtype: int
506 067dda99 Vangelis Koukis
    @return: job id
507 067dda99 Vangelis Koukis

508 067dda99 Vangelis Koukis
    """
509 067dda99 Vangelis Koukis
    body = kwargs
510 067dda99 Vangelis Koukis
511 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
512 067dda99 Vangelis Koukis
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
513 067dda99 Vangelis Koukis
514 e5e20779 Faidon Liambotis
  def GetClusterTags(self):
515 e5e20779 Faidon Liambotis
    """Gets the cluster tags.
516 e5e20779 Faidon Liambotis

517 e5e20779 Faidon Liambotis
    @rtype: list of str
518 e5e20779 Faidon Liambotis
    @return: cluster tags
519 e5e20779 Faidon Liambotis

520 e5e20779 Faidon Liambotis
    """
521 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
522 e5e20779 Faidon Liambotis
                             None, None)
523 e5e20779 Faidon Liambotis
524 e5e20779 Faidon Liambotis
  def AddClusterTags(self, tags, dry_run=False):
525 e5e20779 Faidon Liambotis
    """Adds tags to the cluster.
526 e5e20779 Faidon Liambotis

527 e5e20779 Faidon Liambotis
    @type tags: list of str
528 e5e20779 Faidon Liambotis
    @param tags: tags to add to the cluster
529 e5e20779 Faidon Liambotis
    @type dry_run: bool
530 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
531 e5e20779 Faidon Liambotis

532 e5e20779 Faidon Liambotis
    @rtype: int
533 e5e20779 Faidon Liambotis
    @return: job id
534 e5e20779 Faidon Liambotis

535 e5e20779 Faidon Liambotis
    """
536 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
537 e5e20779 Faidon Liambotis
    if dry_run:
538 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
539 e5e20779 Faidon Liambotis
540 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
541 e5e20779 Faidon Liambotis
                             query, None)
542 e5e20779 Faidon Liambotis
543 e5e20779 Faidon Liambotis
  def DeleteClusterTags(self, tags, dry_run=False):
544 e5e20779 Faidon Liambotis
    """Deletes tags from the cluster.
545 e5e20779 Faidon Liambotis

546 e5e20779 Faidon Liambotis
    @type tags: list of str
547 e5e20779 Faidon Liambotis
    @param tags: tags to delete
548 e5e20779 Faidon Liambotis
    @type dry_run: bool
549 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
550 e5e20779 Faidon Liambotis

551 e5e20779 Faidon Liambotis
    """
552 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
553 e5e20779 Faidon Liambotis
    if dry_run:
554 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
555 e5e20779 Faidon Liambotis
556 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
557 e5e20779 Faidon Liambotis
                             query, None)
558 e5e20779 Faidon Liambotis
559 e5e20779 Faidon Liambotis
  def GetInstances(self, bulk=False):
560 e5e20779 Faidon Liambotis
    """Gets information about instances on the cluster.
561 e5e20779 Faidon Liambotis

562 e5e20779 Faidon Liambotis
    @type bulk: bool
563 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
564 e5e20779 Faidon Liambotis

565 e5e20779 Faidon Liambotis
    @rtype: list of dict or list of str
566 e5e20779 Faidon Liambotis
    @return: if bulk is True, info about the instances, else a list of instances
567 e5e20779 Faidon Liambotis

568 e5e20779 Faidon Liambotis
    """
569 e5e20779 Faidon Liambotis
    query = []
570 e5e20779 Faidon Liambotis
    if bulk:
571 e5e20779 Faidon Liambotis
      query.append(("bulk", 1))
572 e5e20779 Faidon Liambotis
573 e5e20779 Faidon Liambotis
    instances = self._SendRequest(HTTP_GET,
574 e5e20779 Faidon Liambotis
                                  "/%s/instances" % GANETI_RAPI_VERSION,
575 e5e20779 Faidon Liambotis
                                  query, None)
576 e5e20779 Faidon Liambotis
    if bulk:
577 e5e20779 Faidon Liambotis
      return instances
578 e5e20779 Faidon Liambotis
    else:
579 e5e20779 Faidon Liambotis
      return [i["id"] for i in instances]
580 e5e20779 Faidon Liambotis
581 e5e20779 Faidon Liambotis
  def GetInstance(self, instance):
582 e5e20779 Faidon Liambotis
    """Gets information about an instance.
583 e5e20779 Faidon Liambotis

584 e5e20779 Faidon Liambotis
    @type instance: str
585 e5e20779 Faidon Liambotis
    @param instance: instance whose info to return
586 e5e20779 Faidon Liambotis

587 e5e20779 Faidon Liambotis
    @rtype: dict
588 e5e20779 Faidon Liambotis
    @return: info about the instance
589 e5e20779 Faidon Liambotis

590 e5e20779 Faidon Liambotis
    """
591 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
592 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
593 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
594 e5e20779 Faidon Liambotis
595 e5e20779 Faidon Liambotis
  def GetInstanceInfo(self, instance, static=None):
596 e5e20779 Faidon Liambotis
    """Gets information about an instance.
597 e5e20779 Faidon Liambotis

598 e5e20779 Faidon Liambotis
    @type instance: string
599 e5e20779 Faidon Liambotis
    @param instance: Instance name
600 e5e20779 Faidon Liambotis
    @rtype: string
601 e5e20779 Faidon Liambotis
    @return: Job ID
602 e5e20779 Faidon Liambotis

603 e5e20779 Faidon Liambotis
    """
604 e5e20779 Faidon Liambotis
    if static is not None:
605 e5e20779 Faidon Liambotis
      query = [("static", static)]
606 e5e20779 Faidon Liambotis
    else:
607 e5e20779 Faidon Liambotis
      query = None
608 e5e20779 Faidon Liambotis
609 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
610 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/info" %
611 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
612 e5e20779 Faidon Liambotis
613 e5e20779 Faidon Liambotis
  def CreateInstance(self, mode, name, disk_template, disks, nics,
614 e5e20779 Faidon Liambotis
                     **kwargs):
615 e5e20779 Faidon Liambotis
    """Creates a new instance.
616 e5e20779 Faidon Liambotis

617 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
618 e5e20779 Faidon Liambotis

619 e5e20779 Faidon Liambotis
    @type mode: string
620 e5e20779 Faidon Liambotis
    @param mode: Instance creation mode
621 e5e20779 Faidon Liambotis
    @type name: string
622 e5e20779 Faidon Liambotis
    @param name: Hostname of the instance to create
623 e5e20779 Faidon Liambotis
    @type disk_template: string
624 e5e20779 Faidon Liambotis
    @param disk_template: Disk template for instance (e.g. plain, diskless,
625 e5e20779 Faidon Liambotis
                          file, or drbd)
626 e5e20779 Faidon Liambotis
    @type disks: list of dicts
627 e5e20779 Faidon Liambotis
    @param disks: List of disk definitions
628 e5e20779 Faidon Liambotis
    @type nics: list of dicts
629 e5e20779 Faidon Liambotis
    @param nics: List of NIC definitions
630 e5e20779 Faidon Liambotis
    @type dry_run: bool
631 e5e20779 Faidon Liambotis
    @keyword dry_run: whether to perform a dry run
632 e5e20779 Faidon Liambotis

633 e5e20779 Faidon Liambotis
    @rtype: int
634 e5e20779 Faidon Liambotis
    @return: job id
635 e5e20779 Faidon Liambotis

636 e5e20779 Faidon Liambotis
    """
637 e5e20779 Faidon Liambotis
    query = []
638 e5e20779 Faidon Liambotis
639 e5e20779 Faidon Liambotis
    if kwargs.get("dry_run"):
640 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
641 e5e20779 Faidon Liambotis
642 e5e20779 Faidon Liambotis
    if _INST_CREATE_REQV1 in self.GetFeatures():
643 e5e20779 Faidon Liambotis
      # All required fields for request data version 1
644 e5e20779 Faidon Liambotis
      body = {
645 e5e20779 Faidon Liambotis
        _REQ_DATA_VERSION_FIELD: 1,
646 e5e20779 Faidon Liambotis
        "mode": mode,
647 e5e20779 Faidon Liambotis
        "name": name,
648 e5e20779 Faidon Liambotis
        "disk_template": disk_template,
649 e5e20779 Faidon Liambotis
        "disks": disks,
650 e5e20779 Faidon Liambotis
        "nics": nics,
651 e5e20779 Faidon Liambotis
        }
652 e5e20779 Faidon Liambotis
653 e5e20779 Faidon Liambotis
      conflicts = set(kwargs.iterkeys()) & set(body.iterkeys())
654 e5e20779 Faidon Liambotis
      if conflicts:
655 e5e20779 Faidon Liambotis
        raise GanetiApiError("Required fields can not be specified as"
656 e5e20779 Faidon Liambotis
                             " keywords: %s" % ", ".join(conflicts))
657 e5e20779 Faidon Liambotis
658 e5e20779 Faidon Liambotis
      body.update((key, value) for key, value in kwargs.iteritems()
659 e5e20779 Faidon Liambotis
                  if key != "dry_run")
660 e5e20779 Faidon Liambotis
    else:
661 e5e20779 Faidon Liambotis
      # Old request format (version 0)
662 e5e20779 Faidon Liambotis
663 e5e20779 Faidon Liambotis
      # The following code must make sure that an exception is raised when an
664 e5e20779 Faidon Liambotis
      # unsupported setting is requested by the caller. Otherwise this can lead
665 e5e20779 Faidon Liambotis
      # to bugs difficult to find. The interface of this function must stay
666 e5e20779 Faidon Liambotis
      # exactly the same for version 0 and 1 (e.g. they aren't allowed to
667 e5e20779 Faidon Liambotis
      # require different data types).
668 e5e20779 Faidon Liambotis
669 e5e20779 Faidon Liambotis
      # Validate disks
670 e5e20779 Faidon Liambotis
      for idx, disk in enumerate(disks):
671 e5e20779 Faidon Liambotis
        unsupported = set(disk.keys()) - _INST_CREATE_V0_DISK_PARAMS
672 e5e20779 Faidon Liambotis
        if unsupported:
673 e5e20779 Faidon Liambotis
          raise GanetiApiError("Server supports request version 0 only, but"
674 e5e20779 Faidon Liambotis
                               " disk %s specifies the unsupported parameters"
675 e5e20779 Faidon Liambotis
                               " %s, allowed are %s" %
676 e5e20779 Faidon Liambotis
                               (idx, unsupported,
677 e5e20779 Faidon Liambotis
                                list(_INST_CREATE_V0_DISK_PARAMS)))
678 e5e20779 Faidon Liambotis
679 e5e20779 Faidon Liambotis
      assert (len(_INST_CREATE_V0_DISK_PARAMS) == 1 and
680 e5e20779 Faidon Liambotis
              "size" in _INST_CREATE_V0_DISK_PARAMS)
681 e5e20779 Faidon Liambotis
      disk_sizes = [disk["size"] for disk in disks]
682 e5e20779 Faidon Liambotis
683 e5e20779 Faidon Liambotis
      # Validate NICs
684 e5e20779 Faidon Liambotis
      if not nics:
685 e5e20779 Faidon Liambotis
        raise GanetiApiError("Server supports request version 0 only, but"
686 e5e20779 Faidon Liambotis
                             " no NIC specified")
687 e5e20779 Faidon Liambotis
      elif len(nics) > 1:
688 e5e20779 Faidon Liambotis
        raise GanetiApiError("Server supports request version 0 only, but"
689 e5e20779 Faidon Liambotis
                             " more than one NIC specified")
690 e5e20779 Faidon Liambotis
691 e5e20779 Faidon Liambotis
      assert len(nics) == 1
692 e5e20779 Faidon Liambotis
693 e5e20779 Faidon Liambotis
      unsupported = set(nics[0].keys()) - _INST_NIC_PARAMS
694 e5e20779 Faidon Liambotis
      if unsupported:
695 e5e20779 Faidon Liambotis
        raise GanetiApiError("Server supports request version 0 only, but"
696 e5e20779 Faidon Liambotis
                             " NIC 0 specifies the unsupported parameters %s,"
697 e5e20779 Faidon Liambotis
                             " allowed are %s" %
698 e5e20779 Faidon Liambotis
                             (unsupported, list(_INST_NIC_PARAMS)))
699 e5e20779 Faidon Liambotis
700 e5e20779 Faidon Liambotis
      # Validate other parameters
701 e5e20779 Faidon Liambotis
      unsupported = (set(kwargs.keys()) - _INST_CREATE_V0_PARAMS -
702 e5e20779 Faidon Liambotis
                     _INST_CREATE_V0_DPARAMS)
703 e5e20779 Faidon Liambotis
      if unsupported:
704 e5e20779 Faidon Liambotis
        allowed = _INST_CREATE_V0_PARAMS.union(_INST_CREATE_V0_DPARAMS)
705 e5e20779 Faidon Liambotis
        raise GanetiApiError("Server supports request version 0 only, but"
706 e5e20779 Faidon Liambotis
                             " the following unsupported parameters are"
707 e5e20779 Faidon Liambotis
                             " specified: %s, allowed are %s" %
708 e5e20779 Faidon Liambotis
                             (unsupported, list(allowed)))
709 e5e20779 Faidon Liambotis
710 e5e20779 Faidon Liambotis
      # All required fields for request data version 0
711 e5e20779 Faidon Liambotis
      body = {
712 e5e20779 Faidon Liambotis
        _REQ_DATA_VERSION_FIELD: 0,
713 e5e20779 Faidon Liambotis
        "name": name,
714 e5e20779 Faidon Liambotis
        "disk_template": disk_template,
715 e5e20779 Faidon Liambotis
        "disks": disk_sizes,
716 e5e20779 Faidon Liambotis
        }
717 e5e20779 Faidon Liambotis
718 e5e20779 Faidon Liambotis
      # NIC fields
719 e5e20779 Faidon Liambotis
      assert len(nics) == 1
720 e5e20779 Faidon Liambotis
      assert not (set(body.keys()) & set(nics[0].keys()))
721 e5e20779 Faidon Liambotis
      body.update(nics[0])
722 e5e20779 Faidon Liambotis
723 e5e20779 Faidon Liambotis
      # Copy supported fields
724 e5e20779 Faidon Liambotis
      assert not (set(body.keys()) & set(kwargs.keys()))
725 e5e20779 Faidon Liambotis
      body.update(dict((key, value) for key, value in kwargs.items()
726 e5e20779 Faidon Liambotis
                       if key in _INST_CREATE_V0_PARAMS))
727 e5e20779 Faidon Liambotis
728 e5e20779 Faidon Liambotis
      # Merge dictionaries
729 e5e20779 Faidon Liambotis
      for i in (value for key, value in kwargs.items()
730 e5e20779 Faidon Liambotis
                if key in _INST_CREATE_V0_DPARAMS):
731 e5e20779 Faidon Liambotis
        assert not (set(body.keys()) & set(i.keys()))
732 e5e20779 Faidon Liambotis
        body.update(i)
733 e5e20779 Faidon Liambotis
734 e5e20779 Faidon Liambotis
      assert not (set(kwargs.keys()) -
735 e5e20779 Faidon Liambotis
                  (_INST_CREATE_V0_PARAMS | _INST_CREATE_V0_DPARAMS))
736 e5e20779 Faidon Liambotis
      assert not (set(body.keys()) & _INST_CREATE_V0_DPARAMS)
737 e5e20779 Faidon Liambotis
738 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
739 e5e20779 Faidon Liambotis
                             query, body)
740 e5e20779 Faidon Liambotis
741 e5e20779 Faidon Liambotis
  def DeleteInstance(self, instance, dry_run=False):
742 e5e20779 Faidon Liambotis
    """Deletes an instance.
743 e5e20779 Faidon Liambotis

744 e5e20779 Faidon Liambotis
    @type instance: str
745 e5e20779 Faidon Liambotis
    @param instance: the instance to delete
746 e5e20779 Faidon Liambotis

747 e5e20779 Faidon Liambotis
    @rtype: int
748 e5e20779 Faidon Liambotis
    @return: job id
749 e5e20779 Faidon Liambotis

750 e5e20779 Faidon Liambotis
    """
751 e5e20779 Faidon Liambotis
    query = []
752 e5e20779 Faidon Liambotis
    if dry_run:
753 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
754 e5e20779 Faidon Liambotis
755 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
756 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
757 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
758 e5e20779 Faidon Liambotis
759 e5e20779 Faidon Liambotis
  def ModifyInstance(self, instance, **kwargs):
760 e5e20779 Faidon Liambotis
    """Modifies an instance.
761 e5e20779 Faidon Liambotis

762 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
763 e5e20779 Faidon Liambotis

764 e5e20779 Faidon Liambotis
    @type instance: string
765 e5e20779 Faidon Liambotis
    @param instance: Instance name
766 e5e20779 Faidon Liambotis
    @rtype: int
767 e5e20779 Faidon Liambotis
    @return: job id
768 e5e20779 Faidon Liambotis

769 e5e20779 Faidon Liambotis
    """
770 e5e20779 Faidon Liambotis
    body = kwargs
771 e5e20779 Faidon Liambotis
772 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
773 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/modify" %
774 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
775 e5e20779 Faidon Liambotis
776 067dda99 Vangelis Koukis
  def ActivateInstanceDisks(self, instance, ignore_size=None):
777 067dda99 Vangelis Koukis
    """Activates an instance's disks.
778 067dda99 Vangelis Koukis

779 067dda99 Vangelis Koukis
    @type instance: string
780 067dda99 Vangelis Koukis
    @param instance: Instance name
781 067dda99 Vangelis Koukis
    @type ignore_size: bool
782 067dda99 Vangelis Koukis
    @param ignore_size: Whether to ignore recorded size
783 067dda99 Vangelis Koukis
    @return: job id
784 067dda99 Vangelis Koukis

785 067dda99 Vangelis Koukis
    """
786 067dda99 Vangelis Koukis
    query = []
787 067dda99 Vangelis Koukis
    if ignore_size:
788 067dda99 Vangelis Koukis
      query.append(("ignore_size", 1))
789 067dda99 Vangelis Koukis
790 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
791 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/activate-disks" %
792 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), query, None)
793 067dda99 Vangelis Koukis
794 067dda99 Vangelis Koukis
  def DeactivateInstanceDisks(self, instance):
795 067dda99 Vangelis Koukis
    """Deactivates an instance's disks.
796 067dda99 Vangelis Koukis

797 067dda99 Vangelis Koukis
    @type instance: string
798 067dda99 Vangelis Koukis
    @param instance: Instance name
799 067dda99 Vangelis Koukis
    @return: job id
800 067dda99 Vangelis Koukis

801 067dda99 Vangelis Koukis
    """
802 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
803 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/deactivate-disks" %
804 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
805 067dda99 Vangelis Koukis
806 067dda99 Vangelis Koukis
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
807 067dda99 Vangelis Koukis
    """Grows a disk of an instance.
808 067dda99 Vangelis Koukis

809 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
810 067dda99 Vangelis Koukis

811 067dda99 Vangelis Koukis
    @type instance: string
812 067dda99 Vangelis Koukis
    @param instance: Instance name
813 067dda99 Vangelis Koukis
    @type disk: integer
814 067dda99 Vangelis Koukis
    @param disk: Disk index
815 067dda99 Vangelis Koukis
    @type amount: integer
816 067dda99 Vangelis Koukis
    @param amount: Grow disk by this amount (MiB)
817 067dda99 Vangelis Koukis
    @type wait_for_sync: bool
818 067dda99 Vangelis Koukis
    @param wait_for_sync: Wait for disk to synchronize
819 067dda99 Vangelis Koukis
    @rtype: int
820 067dda99 Vangelis Koukis
    @return: job id
821 067dda99 Vangelis Koukis

822 067dda99 Vangelis Koukis
    """
823 067dda99 Vangelis Koukis
    body = {
824 067dda99 Vangelis Koukis
      "amount": amount,
825 067dda99 Vangelis Koukis
      }
826 067dda99 Vangelis Koukis
827 067dda99 Vangelis Koukis
    if wait_for_sync is not None:
828 067dda99 Vangelis Koukis
      body["wait_for_sync"] = wait_for_sync
829 067dda99 Vangelis Koukis
830 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST,
831 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/disk/%s/grow" %
832 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance, disk)),
833 067dda99 Vangelis Koukis
                             None, body)
834 067dda99 Vangelis Koukis
835 e5e20779 Faidon Liambotis
  def GetInstanceTags(self, instance):
836 e5e20779 Faidon Liambotis
    """Gets tags for an instance.
837 e5e20779 Faidon Liambotis

838 e5e20779 Faidon Liambotis
    @type instance: str
839 e5e20779 Faidon Liambotis
    @param instance: instance whose tags to return
840 e5e20779 Faidon Liambotis

841 e5e20779 Faidon Liambotis
    @rtype: list of str
842 e5e20779 Faidon Liambotis
    @return: tags for the instance
843 e5e20779 Faidon Liambotis

844 e5e20779 Faidon Liambotis
    """
845 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
846 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
847 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
848 e5e20779 Faidon Liambotis
849 e5e20779 Faidon Liambotis
  def AddInstanceTags(self, instance, tags, dry_run=False):
850 e5e20779 Faidon Liambotis
    """Adds tags to an instance.
851 e5e20779 Faidon Liambotis

852 e5e20779 Faidon Liambotis
    @type instance: str
853 e5e20779 Faidon Liambotis
    @param instance: instance to add tags to
854 e5e20779 Faidon Liambotis
    @type tags: list of str
855 e5e20779 Faidon Liambotis
    @param tags: tags to add to the instance
856 e5e20779 Faidon Liambotis
    @type dry_run: bool
857 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
858 e5e20779 Faidon Liambotis

859 e5e20779 Faidon Liambotis
    @rtype: int
860 e5e20779 Faidon Liambotis
    @return: job id
861 e5e20779 Faidon Liambotis

862 e5e20779 Faidon Liambotis
    """
863 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
864 e5e20779 Faidon Liambotis
    if dry_run:
865 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
866 e5e20779 Faidon Liambotis
867 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
868 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
869 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
870 e5e20779 Faidon Liambotis
871 e5e20779 Faidon Liambotis
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
872 e5e20779 Faidon Liambotis
    """Deletes tags from an instance.
873 e5e20779 Faidon Liambotis

874 e5e20779 Faidon Liambotis
    @type instance: str
875 e5e20779 Faidon Liambotis
    @param instance: instance to delete tags from
876 e5e20779 Faidon Liambotis
    @type tags: list of str
877 e5e20779 Faidon Liambotis
    @param tags: tags to delete
878 e5e20779 Faidon Liambotis
    @type dry_run: bool
879 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
880 e5e20779 Faidon Liambotis

881 e5e20779 Faidon Liambotis
    """
882 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
883 e5e20779 Faidon Liambotis
    if dry_run:
884 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
885 e5e20779 Faidon Liambotis
886 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
887 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
888 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
889 e5e20779 Faidon Liambotis
890 e5e20779 Faidon Liambotis
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
891 e5e20779 Faidon Liambotis
                     dry_run=False):
892 e5e20779 Faidon Liambotis
    """Reboots an instance.
893 e5e20779 Faidon Liambotis

894 e5e20779 Faidon Liambotis
    @type instance: str
895 e5e20779 Faidon Liambotis
    @param instance: instance to rebot
896 e5e20779 Faidon Liambotis
    @type reboot_type: str
897 e5e20779 Faidon Liambotis
    @param reboot_type: one of: hard, soft, full
898 e5e20779 Faidon Liambotis
    @type ignore_secondaries: bool
899 e5e20779 Faidon Liambotis
    @param ignore_secondaries: if True, ignores errors for the secondary node
900 e5e20779 Faidon Liambotis
        while re-assembling disks (in hard-reboot mode only)
901 e5e20779 Faidon Liambotis
    @type dry_run: bool
902 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
903 e5e20779 Faidon Liambotis

904 e5e20779 Faidon Liambotis
    """
905 e5e20779 Faidon Liambotis
    query = []
906 e5e20779 Faidon Liambotis
    if reboot_type:
907 e5e20779 Faidon Liambotis
      query.append(("type", reboot_type))
908 e5e20779 Faidon Liambotis
    if ignore_secondaries is not None:
909 e5e20779 Faidon Liambotis
      query.append(("ignore_secondaries", ignore_secondaries))
910 e5e20779 Faidon Liambotis
    if dry_run:
911 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
912 e5e20779 Faidon Liambotis
913 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
914 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/reboot" %
915 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
916 e5e20779 Faidon Liambotis
917 e5e20779 Faidon Liambotis
  def ShutdownInstance(self, instance, dry_run=False):
918 e5e20779 Faidon Liambotis
    """Shuts down an instance.
919 e5e20779 Faidon Liambotis

920 e5e20779 Faidon Liambotis
    @type instance: str
921 e5e20779 Faidon Liambotis
    @param instance: the instance to shut down
922 e5e20779 Faidon Liambotis
    @type dry_run: bool
923 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
924 e5e20779 Faidon Liambotis

925 e5e20779 Faidon Liambotis
    """
926 e5e20779 Faidon Liambotis
    query = []
927 e5e20779 Faidon Liambotis
    if dry_run:
928 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
929 e5e20779 Faidon Liambotis
930 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
931 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/shutdown" %
932 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
933 e5e20779 Faidon Liambotis
934 e5e20779 Faidon Liambotis
  def StartupInstance(self, instance, dry_run=False):
935 e5e20779 Faidon Liambotis
    """Starts up an instance.
936 e5e20779 Faidon Liambotis

937 e5e20779 Faidon Liambotis
    @type instance: str
938 e5e20779 Faidon Liambotis
    @param instance: the instance to start up
939 e5e20779 Faidon Liambotis
    @type dry_run: bool
940 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
941 e5e20779 Faidon Liambotis

942 e5e20779 Faidon Liambotis
    """
943 e5e20779 Faidon Liambotis
    query = []
944 e5e20779 Faidon Liambotis
    if dry_run:
945 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
946 e5e20779 Faidon Liambotis
947 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
948 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/startup" %
949 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
950 e5e20779 Faidon Liambotis
951 067dda99 Vangelis Koukis
  def ReinstallInstance(self, instance, os=None, no_startup=False,
952 067dda99 Vangelis Koukis
                        osparams=None):
953 e5e20779 Faidon Liambotis
    """Reinstalls an instance.
954 e5e20779 Faidon Liambotis

955 e5e20779 Faidon Liambotis
    @type instance: str
956 e5e20779 Faidon Liambotis
    @param instance: The instance to reinstall
957 e5e20779 Faidon Liambotis
    @type os: str or None
958 e5e20779 Faidon Liambotis
    @param os: The operating system to reinstall. If None, the instance's
959 e5e20779 Faidon Liambotis
        current operating system will be installed again
960 e5e20779 Faidon Liambotis
    @type no_startup: bool
961 e5e20779 Faidon Liambotis
    @param no_startup: Whether to start the instance automatically
962 e5e20779 Faidon Liambotis

963 e5e20779 Faidon Liambotis
    """
964 067dda99 Vangelis Koukis
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
965 067dda99 Vangelis Koukis
      body = {
966 067dda99 Vangelis Koukis
        "start": not no_startup,
967 067dda99 Vangelis Koukis
        }
968 067dda99 Vangelis Koukis
      if os is not None:
969 067dda99 Vangelis Koukis
        body["os"] = os
970 067dda99 Vangelis Koukis
      if osparams is not None:
971 067dda99 Vangelis Koukis
        body["osparams"] = osparams
972 067dda99 Vangelis Koukis
      return self._SendRequest(HTTP_POST,
973 067dda99 Vangelis Koukis
                               ("/%s/instances/%s/reinstall" %
974 067dda99 Vangelis Koukis
                                (GANETI_RAPI_VERSION, instance)), None, body)
975 067dda99 Vangelis Koukis
976 067dda99 Vangelis Koukis
    # Use old request format
977 067dda99 Vangelis Koukis
    if osparams:
978 067dda99 Vangelis Koukis
      raise GanetiApiError("Server does not support specifying OS parameters"
979 067dda99 Vangelis Koukis
                           " for instance reinstallation")
980 067dda99 Vangelis Koukis
981 e5e20779 Faidon Liambotis
    query = []
982 e5e20779 Faidon Liambotis
    if os:
983 e5e20779 Faidon Liambotis
      query.append(("os", os))
984 e5e20779 Faidon Liambotis
    if no_startup:
985 e5e20779 Faidon Liambotis
      query.append(("nostartup", 1))
986 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
987 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/reinstall" %
988 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
989 e5e20779 Faidon Liambotis
990 e5e20779 Faidon Liambotis
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
991 e5e20779 Faidon Liambotis
                           remote_node=None, iallocator=None, dry_run=False):
992 e5e20779 Faidon Liambotis
    """Replaces disks on an instance.
993 e5e20779 Faidon Liambotis

994 e5e20779 Faidon Liambotis
    @type instance: str
995 e5e20779 Faidon Liambotis
    @param instance: instance whose disks to replace
996 e5e20779 Faidon Liambotis
    @type disks: list of ints
997 e5e20779 Faidon Liambotis
    @param disks: Indexes of disks to replace
998 e5e20779 Faidon Liambotis
    @type mode: str
999 e5e20779 Faidon Liambotis
    @param mode: replacement mode to use (defaults to replace_auto)
1000 e5e20779 Faidon Liambotis
    @type remote_node: str or None
1001 e5e20779 Faidon Liambotis
    @param remote_node: new secondary node to use (for use with
1002 e5e20779 Faidon Liambotis
        replace_new_secondary mode)
1003 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1004 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator plugin to use (for use with
1005 e5e20779 Faidon Liambotis
                       replace_auto mode)
1006 e5e20779 Faidon Liambotis
    @type dry_run: bool
1007 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1008 e5e20779 Faidon Liambotis

1009 e5e20779 Faidon Liambotis
    @rtype: int
1010 e5e20779 Faidon Liambotis
    @return: job id
1011 e5e20779 Faidon Liambotis

1012 e5e20779 Faidon Liambotis
    """
1013 e5e20779 Faidon Liambotis
    query = [
1014 e5e20779 Faidon Liambotis
      ("mode", mode),
1015 e5e20779 Faidon Liambotis
      ]
1016 e5e20779 Faidon Liambotis
1017 e5e20779 Faidon Liambotis
    if disks:
1018 e5e20779 Faidon Liambotis
      query.append(("disks", ",".join(str(idx) for idx in disks)))
1019 e5e20779 Faidon Liambotis
1020 e5e20779 Faidon Liambotis
    if remote_node:
1021 e5e20779 Faidon Liambotis
      query.append(("remote_node", remote_node))
1022 e5e20779 Faidon Liambotis
1023 e5e20779 Faidon Liambotis
    if iallocator:
1024 e5e20779 Faidon Liambotis
      query.append(("iallocator", iallocator))
1025 e5e20779 Faidon Liambotis
1026 e5e20779 Faidon Liambotis
    if dry_run:
1027 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1028 e5e20779 Faidon Liambotis
1029 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1030 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/replace-disks" %
1031 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1032 e5e20779 Faidon Liambotis
1033 e5e20779 Faidon Liambotis
  def PrepareExport(self, instance, mode):
1034 e5e20779 Faidon Liambotis
    """Prepares an instance for an export.
1035 e5e20779 Faidon Liambotis

1036 e5e20779 Faidon Liambotis
    @type instance: string
1037 e5e20779 Faidon Liambotis
    @param instance: Instance name
1038 e5e20779 Faidon Liambotis
    @type mode: string
1039 e5e20779 Faidon Liambotis
    @param mode: Export mode
1040 e5e20779 Faidon Liambotis
    @rtype: string
1041 e5e20779 Faidon Liambotis
    @return: Job ID
1042 e5e20779 Faidon Liambotis

1043 e5e20779 Faidon Liambotis
    """
1044 e5e20779 Faidon Liambotis
    query = [("mode", mode)]
1045 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1046 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/prepare-export" %
1047 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1048 e5e20779 Faidon Liambotis
1049 e5e20779 Faidon Liambotis
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1050 e5e20779 Faidon Liambotis
                     remove_instance=None,
1051 e5e20779 Faidon Liambotis
                     x509_key_name=None, destination_x509_ca=None):
1052 e5e20779 Faidon Liambotis
    """Exports an instance.
1053 e5e20779 Faidon Liambotis

1054 e5e20779 Faidon Liambotis
    @type instance: string
1055 e5e20779 Faidon Liambotis
    @param instance: Instance name
1056 e5e20779 Faidon Liambotis
    @type mode: string
1057 e5e20779 Faidon Liambotis
    @param mode: Export mode
1058 e5e20779 Faidon Liambotis
    @rtype: string
1059 e5e20779 Faidon Liambotis
    @return: Job ID
1060 e5e20779 Faidon Liambotis

1061 e5e20779 Faidon Liambotis
    """
1062 e5e20779 Faidon Liambotis
    body = {
1063 e5e20779 Faidon Liambotis
      "destination": destination,
1064 e5e20779 Faidon Liambotis
      "mode": mode,
1065 e5e20779 Faidon Liambotis
      }
1066 e5e20779 Faidon Liambotis
1067 e5e20779 Faidon Liambotis
    if shutdown is not None:
1068 e5e20779 Faidon Liambotis
      body["shutdown"] = shutdown
1069 e5e20779 Faidon Liambotis
1070 e5e20779 Faidon Liambotis
    if remove_instance is not None:
1071 e5e20779 Faidon Liambotis
      body["remove_instance"] = remove_instance
1072 e5e20779 Faidon Liambotis
1073 e5e20779 Faidon Liambotis
    if x509_key_name is not None:
1074 e5e20779 Faidon Liambotis
      body["x509_key_name"] = x509_key_name
1075 e5e20779 Faidon Liambotis
1076 e5e20779 Faidon Liambotis
    if destination_x509_ca is not None:
1077 e5e20779 Faidon Liambotis
      body["destination_x509_ca"] = destination_x509_ca
1078 e5e20779 Faidon Liambotis
1079 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1080 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/export" %
1081 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1082 e5e20779 Faidon Liambotis
1083 e5e20779 Faidon Liambotis
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1084 e5e20779 Faidon Liambotis
    """Migrates an instance.
1085 e5e20779 Faidon Liambotis

1086 e5e20779 Faidon Liambotis
    @type instance: string
1087 e5e20779 Faidon Liambotis
    @param instance: Instance name
1088 e5e20779 Faidon Liambotis
    @type mode: string
1089 e5e20779 Faidon Liambotis
    @param mode: Migration mode
1090 e5e20779 Faidon Liambotis
    @type cleanup: bool
1091 e5e20779 Faidon Liambotis
    @param cleanup: Whether to clean up a previously failed migration
1092 e5e20779 Faidon Liambotis

1093 e5e20779 Faidon Liambotis
    """
1094 e5e20779 Faidon Liambotis
    body = {}
1095 e5e20779 Faidon Liambotis
1096 e5e20779 Faidon Liambotis
    if mode is not None:
1097 e5e20779 Faidon Liambotis
      body["mode"] = mode
1098 e5e20779 Faidon Liambotis
1099 e5e20779 Faidon Liambotis
    if cleanup is not None:
1100 e5e20779 Faidon Liambotis
      body["cleanup"] = cleanup
1101 e5e20779 Faidon Liambotis
1102 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1103 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/migrate" %
1104 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1105 e5e20779 Faidon Liambotis
1106 e5e20779 Faidon Liambotis
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1107 e5e20779 Faidon Liambotis
    """Changes the name of an instance.
1108 e5e20779 Faidon Liambotis

1109 e5e20779 Faidon Liambotis
    @type instance: string
1110 e5e20779 Faidon Liambotis
    @param instance: Instance name
1111 e5e20779 Faidon Liambotis
    @type new_name: string
1112 e5e20779 Faidon Liambotis
    @param new_name: New instance name
1113 e5e20779 Faidon Liambotis
    @type ip_check: bool
1114 e5e20779 Faidon Liambotis
    @param ip_check: Whether to ensure instance's IP address is inactive
1115 e5e20779 Faidon Liambotis
    @type name_check: bool
1116 e5e20779 Faidon Liambotis
    @param name_check: Whether to ensure instance's name is resolvable
1117 e5e20779 Faidon Liambotis

1118 e5e20779 Faidon Liambotis
    """
1119 e5e20779 Faidon Liambotis
    body = {
1120 e5e20779 Faidon Liambotis
      "new_name": new_name,
1121 e5e20779 Faidon Liambotis
      }
1122 e5e20779 Faidon Liambotis
1123 e5e20779 Faidon Liambotis
    if ip_check is not None:
1124 e5e20779 Faidon Liambotis
      body["ip_check"] = ip_check
1125 e5e20779 Faidon Liambotis
1126 e5e20779 Faidon Liambotis
    if name_check is not None:
1127 e5e20779 Faidon Liambotis
      body["name_check"] = name_check
1128 e5e20779 Faidon Liambotis
1129 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1130 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/rename" %
1131 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1132 e5e20779 Faidon Liambotis
1133 067dda99 Vangelis Koukis
  def GetInstanceConsole(self, instance):
1134 067dda99 Vangelis Koukis
    """Request information for connecting to instance's console.
1135 067dda99 Vangelis Koukis

1136 067dda99 Vangelis Koukis
    @type instance: string
1137 067dda99 Vangelis Koukis
    @param instance: Instance name
1138 067dda99 Vangelis Koukis

1139 067dda99 Vangelis Koukis
    """
1140 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1141 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/console" %
1142 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
1143 067dda99 Vangelis Koukis
1144 e5e20779 Faidon Liambotis
  def GetJobs(self):
1145 e5e20779 Faidon Liambotis
    """Gets all jobs for the cluster.
1146 e5e20779 Faidon Liambotis

1147 e5e20779 Faidon Liambotis
    @rtype: list of int
1148 e5e20779 Faidon Liambotis
    @return: job ids for the cluster
1149 e5e20779 Faidon Liambotis

1150 e5e20779 Faidon Liambotis
    """
1151 e5e20779 Faidon Liambotis
    return [int(j["id"])
1152 e5e20779 Faidon Liambotis
            for j in self._SendRequest(HTTP_GET,
1153 e5e20779 Faidon Liambotis
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1154 e5e20779 Faidon Liambotis
                                       None, None)]
1155 e5e20779 Faidon Liambotis
1156 e5e20779 Faidon Liambotis
  def GetJobStatus(self, job_id):
1157 e5e20779 Faidon Liambotis
    """Gets the status of a job.
1158 e5e20779 Faidon Liambotis

1159 e5e20779 Faidon Liambotis
    @type job_id: int
1160 e5e20779 Faidon Liambotis
    @param job_id: job id whose status to query
1161 e5e20779 Faidon Liambotis

1162 e5e20779 Faidon Liambotis
    @rtype: dict
1163 e5e20779 Faidon Liambotis
    @return: job status
1164 e5e20779 Faidon Liambotis

1165 e5e20779 Faidon Liambotis
    """
1166 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1167 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1168 e5e20779 Faidon Liambotis
                             None, None)
1169 e5e20779 Faidon Liambotis
1170 e5e20779 Faidon Liambotis
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1171 e5e20779 Faidon Liambotis
    """Waits for job changes.
1172 e5e20779 Faidon Liambotis

1173 e5e20779 Faidon Liambotis
    @type job_id: int
1174 e5e20779 Faidon Liambotis
    @param job_id: Job ID for which to wait
1175 e5e20779 Faidon Liambotis

1176 e5e20779 Faidon Liambotis
    """
1177 e5e20779 Faidon Liambotis
    body = {
1178 e5e20779 Faidon Liambotis
      "fields": fields,
1179 e5e20779 Faidon Liambotis
      "previous_job_info": prev_job_info,
1180 e5e20779 Faidon Liambotis
      "previous_log_serial": prev_log_serial,
1181 e5e20779 Faidon Liambotis
      }
1182 e5e20779 Faidon Liambotis
1183 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1184 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1185 e5e20779 Faidon Liambotis
                             None, body)
1186 e5e20779 Faidon Liambotis
1187 e5e20779 Faidon Liambotis
  def CancelJob(self, job_id, dry_run=False):
1188 e5e20779 Faidon Liambotis
    """Cancels a job.
1189 e5e20779 Faidon Liambotis

1190 e5e20779 Faidon Liambotis
    @type job_id: int
1191 e5e20779 Faidon Liambotis
    @param job_id: id of the job to delete
1192 e5e20779 Faidon Liambotis
    @type dry_run: bool
1193 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1194 e5e20779 Faidon Liambotis

1195 e5e20779 Faidon Liambotis
    """
1196 e5e20779 Faidon Liambotis
    query = []
1197 e5e20779 Faidon Liambotis
    if dry_run:
1198 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1199 e5e20779 Faidon Liambotis
1200 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1201 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1202 e5e20779 Faidon Liambotis
                             query, None)
1203 e5e20779 Faidon Liambotis
1204 e5e20779 Faidon Liambotis
  def GetNodes(self, bulk=False):
1205 e5e20779 Faidon Liambotis
    """Gets all nodes in the cluster.
1206 e5e20779 Faidon Liambotis

1207 e5e20779 Faidon Liambotis
    @type bulk: bool
1208 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
1209 e5e20779 Faidon Liambotis

1210 e5e20779 Faidon Liambotis
    @rtype: list of dict or str
1211 e5e20779 Faidon Liambotis
    @return: if bulk is true, info about nodes in the cluster,
1212 e5e20779 Faidon Liambotis
        else list of nodes in the cluster
1213 e5e20779 Faidon Liambotis

1214 e5e20779 Faidon Liambotis
    """
1215 e5e20779 Faidon Liambotis
    query = []
1216 e5e20779 Faidon Liambotis
    if bulk:
1217 e5e20779 Faidon Liambotis
      query.append(("bulk", 1))
1218 e5e20779 Faidon Liambotis
1219 e5e20779 Faidon Liambotis
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1220 e5e20779 Faidon Liambotis
                              query, None)
1221 e5e20779 Faidon Liambotis
    if bulk:
1222 e5e20779 Faidon Liambotis
      return nodes
1223 e5e20779 Faidon Liambotis
    else:
1224 e5e20779 Faidon Liambotis
      return [n["id"] for n in nodes]
1225 e5e20779 Faidon Liambotis
1226 e5e20779 Faidon Liambotis
  def GetNode(self, node):
1227 e5e20779 Faidon Liambotis
    """Gets information about a node.
1228 e5e20779 Faidon Liambotis

1229 e5e20779 Faidon Liambotis
    @type node: str
1230 e5e20779 Faidon Liambotis
    @param node: node whose info to return
1231 e5e20779 Faidon Liambotis

1232 e5e20779 Faidon Liambotis
    @rtype: dict
1233 e5e20779 Faidon Liambotis
    @return: info about the node
1234 e5e20779 Faidon Liambotis

1235 e5e20779 Faidon Liambotis
    """
1236 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1237 e5e20779 Faidon Liambotis
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1238 e5e20779 Faidon Liambotis
                             None, None)
1239 e5e20779 Faidon Liambotis
1240 e5e20779 Faidon Liambotis
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1241 e5e20779 Faidon Liambotis
                   dry_run=False, early_release=False):
1242 e5e20779 Faidon Liambotis
    """Evacuates instances from a Ganeti node.
1243 e5e20779 Faidon Liambotis

1244 e5e20779 Faidon Liambotis
    @type node: str
1245 e5e20779 Faidon Liambotis
    @param node: node to evacuate
1246 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1247 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator to use
1248 e5e20779 Faidon Liambotis
    @type remote_node: str
1249 e5e20779 Faidon Liambotis
    @param remote_node: node to evaucate to
1250 e5e20779 Faidon Liambotis
    @type dry_run: bool
1251 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1252 e5e20779 Faidon Liambotis
    @type early_release: bool
1253 e5e20779 Faidon Liambotis
    @param early_release: whether to enable parallelization
1254 e5e20779 Faidon Liambotis

1255 e5e20779 Faidon Liambotis
    @rtype: list
1256 e5e20779 Faidon Liambotis
    @return: list of (job ID, instance name, new secondary node); if
1257 e5e20779 Faidon Liambotis
        dry_run was specified, then the actual move jobs were not
1258 e5e20779 Faidon Liambotis
        submitted and the job IDs will be C{None}
1259 e5e20779 Faidon Liambotis

1260 e5e20779 Faidon Liambotis
    @raises GanetiApiError: if an iallocator and remote_node are both
1261 e5e20779 Faidon Liambotis
        specified
1262 e5e20779 Faidon Liambotis

1263 e5e20779 Faidon Liambotis
    """
1264 e5e20779 Faidon Liambotis
    if iallocator and remote_node:
1265 e5e20779 Faidon Liambotis
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1266 e5e20779 Faidon Liambotis
1267 e5e20779 Faidon Liambotis
    query = []
1268 e5e20779 Faidon Liambotis
    if iallocator:
1269 e5e20779 Faidon Liambotis
      query.append(("iallocator", iallocator))
1270 e5e20779 Faidon Liambotis
    if remote_node:
1271 e5e20779 Faidon Liambotis
      query.append(("remote_node", remote_node))
1272 e5e20779 Faidon Liambotis
    if dry_run:
1273 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1274 e5e20779 Faidon Liambotis
    if early_release:
1275 e5e20779 Faidon Liambotis
      query.append(("early_release", 1))
1276 e5e20779 Faidon Liambotis
1277 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1278 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/evacuate" %
1279 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1280 e5e20779 Faidon Liambotis
1281 e5e20779 Faidon Liambotis
  def MigrateNode(self, node, mode=None, dry_run=False):
1282 e5e20779 Faidon Liambotis
    """Migrates all primary instances from a node.
1283 e5e20779 Faidon Liambotis

1284 e5e20779 Faidon Liambotis
    @type node: str
1285 e5e20779 Faidon Liambotis
    @param node: node to migrate
1286 e5e20779 Faidon Liambotis
    @type mode: string
1287 e5e20779 Faidon Liambotis
    @param mode: if passed, it will overwrite the live migration type,
1288 e5e20779 Faidon Liambotis
        otherwise the hypervisor default will be used
1289 e5e20779 Faidon Liambotis
    @type dry_run: bool
1290 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1291 e5e20779 Faidon Liambotis

1292 e5e20779 Faidon Liambotis
    @rtype: int
1293 e5e20779 Faidon Liambotis
    @return: job id
1294 e5e20779 Faidon Liambotis

1295 e5e20779 Faidon Liambotis
    """
1296 e5e20779 Faidon Liambotis
    query = []
1297 e5e20779 Faidon Liambotis
    if mode is not None:
1298 e5e20779 Faidon Liambotis
      query.append(("mode", mode))
1299 e5e20779 Faidon Liambotis
    if dry_run:
1300 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1301 e5e20779 Faidon Liambotis
1302 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1303 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/migrate" %
1304 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1305 e5e20779 Faidon Liambotis
1306 e5e20779 Faidon Liambotis
  def GetNodeRole(self, node):
1307 e5e20779 Faidon Liambotis
    """Gets the current role for a node.
1308 e5e20779 Faidon Liambotis

1309 e5e20779 Faidon Liambotis
    @type node: str
1310 e5e20779 Faidon Liambotis
    @param node: node whose role to return
1311 e5e20779 Faidon Liambotis

1312 e5e20779 Faidon Liambotis
    @rtype: str
1313 e5e20779 Faidon Liambotis
    @return: the current role for a node
1314 e5e20779 Faidon Liambotis

1315 e5e20779 Faidon Liambotis
    """
1316 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1317 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1318 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1319 e5e20779 Faidon Liambotis
1320 e5e20779 Faidon Liambotis
  def SetNodeRole(self, node, role, force=False):
1321 e5e20779 Faidon Liambotis
    """Sets the role for a node.
1322 e5e20779 Faidon Liambotis

1323 e5e20779 Faidon Liambotis
    @type node: str
1324 e5e20779 Faidon Liambotis
    @param node: the node whose role to set
1325 e5e20779 Faidon Liambotis
    @type role: str
1326 e5e20779 Faidon Liambotis
    @param role: the role to set for the node
1327 e5e20779 Faidon Liambotis
    @type force: bool
1328 e5e20779 Faidon Liambotis
    @param force: whether to force the role change
1329 e5e20779 Faidon Liambotis

1330 e5e20779 Faidon Liambotis
    @rtype: int
1331 e5e20779 Faidon Liambotis
    @return: job id
1332 e5e20779 Faidon Liambotis

1333 e5e20779 Faidon Liambotis
    """
1334 e5e20779 Faidon Liambotis
    query = [
1335 e5e20779 Faidon Liambotis
      ("force", force),
1336 e5e20779 Faidon Liambotis
      ]
1337 e5e20779 Faidon Liambotis
1338 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1339 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1340 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, role)
1341 e5e20779 Faidon Liambotis
1342 e5e20779 Faidon Liambotis
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1343 e5e20779 Faidon Liambotis
    """Gets the storage units for a node.
1344 e5e20779 Faidon Liambotis

1345 e5e20779 Faidon Liambotis
    @type node: str
1346 e5e20779 Faidon Liambotis
    @param node: the node whose storage units to return
1347 e5e20779 Faidon Liambotis
    @type storage_type: str
1348 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to return
1349 e5e20779 Faidon Liambotis
    @type output_fields: str
1350 e5e20779 Faidon Liambotis
    @param output_fields: storage type fields to return
1351 e5e20779 Faidon Liambotis

1352 e5e20779 Faidon Liambotis
    @rtype: int
1353 e5e20779 Faidon Liambotis
    @return: job id where results can be retrieved
1354 e5e20779 Faidon Liambotis

1355 e5e20779 Faidon Liambotis
    """
1356 e5e20779 Faidon Liambotis
    query = [
1357 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1358 e5e20779 Faidon Liambotis
      ("output_fields", output_fields),
1359 e5e20779 Faidon Liambotis
      ]
1360 e5e20779 Faidon Liambotis
1361 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1362 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage" %
1363 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1364 e5e20779 Faidon Liambotis
1365 e5e20779 Faidon Liambotis
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1366 e5e20779 Faidon Liambotis
    """Modifies parameters of storage units on the node.
1367 e5e20779 Faidon Liambotis

1368 e5e20779 Faidon Liambotis
    @type node: str
1369 e5e20779 Faidon Liambotis
    @param node: node whose storage units to modify
1370 e5e20779 Faidon Liambotis
    @type storage_type: str
1371 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to modify
1372 e5e20779 Faidon Liambotis
    @type name: str
1373 e5e20779 Faidon Liambotis
    @param name: name of the storage unit
1374 e5e20779 Faidon Liambotis
    @type allocatable: bool or None
1375 e5e20779 Faidon Liambotis
    @param allocatable: Whether to set the "allocatable" flag on the storage
1376 e5e20779 Faidon Liambotis
                        unit (None=no modification, True=set, False=unset)
1377 e5e20779 Faidon Liambotis

1378 e5e20779 Faidon Liambotis
    @rtype: int
1379 e5e20779 Faidon Liambotis
    @return: job id
1380 e5e20779 Faidon Liambotis

1381 e5e20779 Faidon Liambotis
    """
1382 e5e20779 Faidon Liambotis
    query = [
1383 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1384 e5e20779 Faidon Liambotis
      ("name", name),
1385 e5e20779 Faidon Liambotis
      ]
1386 e5e20779 Faidon Liambotis
1387 e5e20779 Faidon Liambotis
    if allocatable is not None:
1388 e5e20779 Faidon Liambotis
      query.append(("allocatable", allocatable))
1389 e5e20779 Faidon Liambotis
1390 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1391 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/modify" %
1392 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1393 e5e20779 Faidon Liambotis
1394 e5e20779 Faidon Liambotis
  def RepairNodeStorageUnits(self, node, storage_type, name):
1395 e5e20779 Faidon Liambotis
    """Repairs a storage unit on the node.
1396 e5e20779 Faidon Liambotis

1397 e5e20779 Faidon Liambotis
    @type node: str
1398 e5e20779 Faidon Liambotis
    @param node: node whose storage units to repair
1399 e5e20779 Faidon Liambotis
    @type storage_type: str
1400 e5e20779 Faidon Liambotis
    @param storage_type: storage type to repair
1401 e5e20779 Faidon Liambotis
    @type name: str
1402 e5e20779 Faidon Liambotis
    @param name: name of the storage unit to repair
1403 e5e20779 Faidon Liambotis

1404 e5e20779 Faidon Liambotis
    @rtype: int
1405 e5e20779 Faidon Liambotis
    @return: job id
1406 e5e20779 Faidon Liambotis

1407 e5e20779 Faidon Liambotis
    """
1408 e5e20779 Faidon Liambotis
    query = [
1409 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1410 e5e20779 Faidon Liambotis
      ("name", name),
1411 e5e20779 Faidon Liambotis
      ]
1412 e5e20779 Faidon Liambotis
1413 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1414 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/repair" %
1415 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1416 e5e20779 Faidon Liambotis
1417 e5e20779 Faidon Liambotis
  def GetNodeTags(self, node):
1418 e5e20779 Faidon Liambotis
    """Gets the tags for a node.
1419 e5e20779 Faidon Liambotis

1420 e5e20779 Faidon Liambotis
    @type node: str
1421 e5e20779 Faidon Liambotis
    @param node: node whose tags to return
1422 e5e20779 Faidon Liambotis

1423 e5e20779 Faidon Liambotis
    @rtype: list of str
1424 e5e20779 Faidon Liambotis
    @return: tags for the node
1425 e5e20779 Faidon Liambotis

1426 e5e20779 Faidon Liambotis
    """
1427 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1428 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1429 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1430 e5e20779 Faidon Liambotis
1431 e5e20779 Faidon Liambotis
  def AddNodeTags(self, node, tags, dry_run=False):
1432 e5e20779 Faidon Liambotis
    """Adds tags to a node.
1433 e5e20779 Faidon Liambotis

1434 e5e20779 Faidon Liambotis
    @type node: str
1435 e5e20779 Faidon Liambotis
    @param node: node to add tags to
1436 e5e20779 Faidon Liambotis
    @type tags: list of str
1437 e5e20779 Faidon Liambotis
    @param tags: tags to add to the node
1438 e5e20779 Faidon Liambotis
    @type dry_run: bool
1439 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1440 e5e20779 Faidon Liambotis

1441 e5e20779 Faidon Liambotis
    @rtype: int
1442 e5e20779 Faidon Liambotis
    @return: job id
1443 e5e20779 Faidon Liambotis

1444 e5e20779 Faidon Liambotis
    """
1445 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1446 e5e20779 Faidon Liambotis
    if dry_run:
1447 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1448 e5e20779 Faidon Liambotis
1449 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1450 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1451 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, tags)
1452 e5e20779 Faidon Liambotis
1453 e5e20779 Faidon Liambotis
  def DeleteNodeTags(self, node, tags, dry_run=False):
1454 e5e20779 Faidon Liambotis
    """Delete tags from a node.
1455 e5e20779 Faidon Liambotis

1456 e5e20779 Faidon Liambotis
    @type node: str
1457 e5e20779 Faidon Liambotis
    @param node: node to remove tags from
1458 e5e20779 Faidon Liambotis
    @type tags: list of str
1459 e5e20779 Faidon Liambotis
    @param tags: tags to remove from the node
1460 e5e20779 Faidon Liambotis
    @type dry_run: bool
1461 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1462 e5e20779 Faidon Liambotis

1463 e5e20779 Faidon Liambotis
    @rtype: int
1464 e5e20779 Faidon Liambotis
    @return: job id
1465 e5e20779 Faidon Liambotis

1466 e5e20779 Faidon Liambotis
    """
1467 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1468 e5e20779 Faidon Liambotis
    if dry_run:
1469 e5e20779 Faidon Liambotis
      query.append(("dry-run", 1))
1470 e5e20779 Faidon Liambotis
1471 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1472 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1473 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1474 067dda99 Vangelis Koukis
1475 067dda99 Vangelis Koukis
  def GetGroups(self, bulk=False):
1476 067dda99 Vangelis Koukis
    """Gets all node groups in the cluster.
1477 067dda99 Vangelis Koukis

1478 067dda99 Vangelis Koukis
    @type bulk: bool
1479 067dda99 Vangelis Koukis
    @param bulk: whether to return all information about the groups
1480 067dda99 Vangelis Koukis

1481 067dda99 Vangelis Koukis
    @rtype: list of dict or str
1482 067dda99 Vangelis Koukis
    @return: if bulk is true, a list of dictionaries with info about all node
1483 067dda99 Vangelis Koukis
        groups in the cluster, else a list of names of those node groups
1484 067dda99 Vangelis Koukis

1485 067dda99 Vangelis Koukis
    """
1486 067dda99 Vangelis Koukis
    query = []
1487 067dda99 Vangelis Koukis
    if bulk:
1488 067dda99 Vangelis Koukis
      query.append(("bulk", 1))
1489 067dda99 Vangelis Koukis
1490 067dda99 Vangelis Koukis
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1491 067dda99 Vangelis Koukis
                               query, None)
1492 067dda99 Vangelis Koukis
    if bulk:
1493 067dda99 Vangelis Koukis
      return groups
1494 067dda99 Vangelis Koukis
    else:
1495 067dda99 Vangelis Koukis
      return [g["name"] for g in groups]
1496 067dda99 Vangelis Koukis
1497 067dda99 Vangelis Koukis
  def GetGroup(self, group):
1498 067dda99 Vangelis Koukis
    """Gets information about a node group.
1499 067dda99 Vangelis Koukis

1500 067dda99 Vangelis Koukis
    @type group: str
1501 067dda99 Vangelis Koukis
    @param group: name of the node group whose info to return
1502 067dda99 Vangelis Koukis

1503 067dda99 Vangelis Koukis
    @rtype: dict
1504 067dda99 Vangelis Koukis
    @return: info about the node group
1505 067dda99 Vangelis Koukis

1506 067dda99 Vangelis Koukis
    """
1507 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1508 067dda99 Vangelis Koukis
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1509 067dda99 Vangelis Koukis
                             None, None)
1510 067dda99 Vangelis Koukis
1511 067dda99 Vangelis Koukis
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1512 067dda99 Vangelis Koukis
    """Creates a new node group.
1513 067dda99 Vangelis Koukis

1514 067dda99 Vangelis Koukis
    @type name: str
1515 067dda99 Vangelis Koukis
    @param name: the name of node group to create
1516 067dda99 Vangelis Koukis
    @type alloc_policy: str
1517 067dda99 Vangelis Koukis
    @param alloc_policy: the desired allocation policy for the group, if any
1518 067dda99 Vangelis Koukis
    @type dry_run: bool
1519 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1520 067dda99 Vangelis Koukis

1521 067dda99 Vangelis Koukis
    @rtype: int
1522 067dda99 Vangelis Koukis
    @return: job id
1523 067dda99 Vangelis Koukis

1524 067dda99 Vangelis Koukis
    """
1525 067dda99 Vangelis Koukis
    query = []
1526 067dda99 Vangelis Koukis
    if dry_run:
1527 067dda99 Vangelis Koukis
      query.append(("dry-run", 1))
1528 067dda99 Vangelis Koukis
1529 067dda99 Vangelis Koukis
    body = {
1530 067dda99 Vangelis Koukis
      "name": name,
1531 067dda99 Vangelis Koukis
      "alloc_policy": alloc_policy
1532 067dda99 Vangelis Koukis
      }
1533 067dda99 Vangelis Koukis
1534 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1535 067dda99 Vangelis Koukis
                             query, body)
1536 067dda99 Vangelis Koukis
1537 067dda99 Vangelis Koukis
  def ModifyGroup(self, group, **kwargs):
1538 067dda99 Vangelis Koukis
    """Modifies a node group.
1539 067dda99 Vangelis Koukis

1540 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
1541 067dda99 Vangelis Koukis

1542 067dda99 Vangelis Koukis
    @type group: string
1543 067dda99 Vangelis Koukis
    @param group: Node group name
1544 067dda99 Vangelis Koukis
    @rtype: int
1545 067dda99 Vangelis Koukis
    @return: job id
1546 067dda99 Vangelis Koukis

1547 067dda99 Vangelis Koukis
    """
1548 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1549 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/modify" %
1550 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1551 067dda99 Vangelis Koukis
1552 067dda99 Vangelis Koukis
  def DeleteGroup(self, group, dry_run=False):
1553 067dda99 Vangelis Koukis
    """Deletes a node group.
1554 067dda99 Vangelis Koukis

1555 067dda99 Vangelis Koukis
    @type group: str
1556 067dda99 Vangelis Koukis
    @param group: the node group to delete
1557 067dda99 Vangelis Koukis
    @type dry_run: bool
1558 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1559 067dda99 Vangelis Koukis

1560 067dda99 Vangelis Koukis
    @rtype: int
1561 067dda99 Vangelis Koukis
    @return: job id
1562 067dda99 Vangelis Koukis

1563 067dda99 Vangelis Koukis
    """
1564 067dda99 Vangelis Koukis
    query = []
1565 067dda99 Vangelis Koukis
    if dry_run:
1566 067dda99 Vangelis Koukis
      query.append(("dry-run", 1))
1567 067dda99 Vangelis Koukis
1568 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_DELETE,
1569 067dda99 Vangelis Koukis
                             ("/%s/groups/%s" %
1570 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), query, None)
1571 067dda99 Vangelis Koukis
1572 067dda99 Vangelis Koukis
  def RenameGroup(self, group, new_name):
1573 067dda99 Vangelis Koukis
    """Changes the name of a node group.
1574 067dda99 Vangelis Koukis

1575 067dda99 Vangelis Koukis
    @type group: string
1576 067dda99 Vangelis Koukis
    @param group: Node group name
1577 067dda99 Vangelis Koukis
    @type new_name: string
1578 067dda99 Vangelis Koukis
    @param new_name: New node group name
1579 067dda99 Vangelis Koukis

1580 067dda99 Vangelis Koukis
    @rtype: int
1581 067dda99 Vangelis Koukis
    @return: job id
1582 067dda99 Vangelis Koukis

1583 067dda99 Vangelis Koukis
    """
1584 067dda99 Vangelis Koukis
    body = {
1585 067dda99 Vangelis Koukis
      "new_name": new_name,
1586 067dda99 Vangelis Koukis
      }
1587 067dda99 Vangelis Koukis
1588 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1589 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/rename" %
1590 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, body)
1591 067dda99 Vangelis Koukis
1592 067dda99 Vangelis Koukis
1593 067dda99 Vangelis Koukis
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
1594 067dda99 Vangelis Koukis
    """Assigns nodes to a group.
1595 067dda99 Vangelis Koukis

1596 067dda99 Vangelis Koukis
    @type group: string
1597 067dda99 Vangelis Koukis
    @param group: Node gropu name
1598 067dda99 Vangelis Koukis
    @type nodes: list of strings
1599 067dda99 Vangelis Koukis
    @param nodes: List of nodes to assign to the group
1600 067dda99 Vangelis Koukis

1601 067dda99 Vangelis Koukis
    @rtype: int
1602 067dda99 Vangelis Koukis
    @return: job id
1603 067dda99 Vangelis Koukis

1604 067dda99 Vangelis Koukis
    """
1605 067dda99 Vangelis Koukis
    query = []
1606 067dda99 Vangelis Koukis
1607 067dda99 Vangelis Koukis
    if force:
1608 067dda99 Vangelis Koukis
      query.append(("force", 1))
1609 067dda99 Vangelis Koukis
1610 067dda99 Vangelis Koukis
    if dry_run:
1611 067dda99 Vangelis Koukis
      query.append(("dry-run", 1))
1612 067dda99 Vangelis Koukis
1613 067dda99 Vangelis Koukis
    body = {
1614 067dda99 Vangelis Koukis
      "nodes": nodes,
1615 067dda99 Vangelis Koukis
      }
1616 067dda99 Vangelis Koukis
1617 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1618 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/assign-nodes" %
1619 067dda99 Vangelis Koukis
                             (GANETI_RAPI_VERSION, group)), query, body)