Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 065be3f0

History | View | Annotate | Download (54.6 kB)

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

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

31 2a7c3583 Michael Hanselmann
"""
32 95ab4de9 David Knowles
33 5ef5cfea Michael Hanselmann
# No Ganeti-specific modules should be imported. The RAPI client is supposed to
34 5ef5cfea Michael Hanselmann
# be standalone.
35 5ef5cfea Michael Hanselmann
36 9279e986 Michael Hanselmann
import logging
37 95ab4de9 David Knowles
import simplejson
38 1a8337f2 Manuel Franceschini
import socket
39 95ab4de9 David Knowles
import urllib
40 2a7c3583 Michael Hanselmann
import threading
41 2a7c3583 Michael Hanselmann
import pycurl
42 16c13387 Theo Van Dinter
import time
43 2a7c3583 Michael Hanselmann
44 2a7c3583 Michael Hanselmann
try:
45 2a7c3583 Michael Hanselmann
  from cStringIO import StringIO
46 2a7c3583 Michael Hanselmann
except ImportError:
47 2a7c3583 Michael Hanselmann
  from StringIO import StringIO
48 95ab4de9 David Knowles
49 95ab4de9 David Knowles
50 9279e986 Michael Hanselmann
GANETI_RAPI_PORT = 5080
51 a198b2d9 Michael Hanselmann
GANETI_RAPI_VERSION = 2
52 9279e986 Michael Hanselmann
53 95ab4de9 David Knowles
HTTP_DELETE = "DELETE"
54 95ab4de9 David Knowles
HTTP_GET = "GET"
55 95ab4de9 David Knowles
HTTP_PUT = "PUT"
56 95ab4de9 David Knowles
HTTP_POST = "POST"
57 9279e986 Michael Hanselmann
HTTP_OK = 200
58 7eac4a4d Michael Hanselmann
HTTP_NOT_FOUND = 404
59 9279e986 Michael Hanselmann
HTTP_APP_JSON = "application/json"
60 9279e986 Michael Hanselmann
61 95ab4de9 David Knowles
REPLACE_DISK_PRI = "replace_on_primary"
62 95ab4de9 David Knowles
REPLACE_DISK_SECONDARY = "replace_on_secondary"
63 95ab4de9 David Knowles
REPLACE_DISK_CHG = "replace_new_secondary"
64 95ab4de9 David Knowles
REPLACE_DISK_AUTO = "replace_auto"
65 1068639f Michael Hanselmann
66 0b58db81 Michael Hanselmann
NODE_EVAC_PRI = "primary-only"
67 0b58db81 Michael Hanselmann
NODE_EVAC_SEC = "secondary-only"
68 0b58db81 Michael Hanselmann
NODE_EVAC_ALL = "all"
69 0b58db81 Michael Hanselmann
70 1068639f Michael Hanselmann
NODE_ROLE_DRAINED = "drained"
71 1068639f Michael Hanselmann
NODE_ROLE_MASTER_CANDIATE = "master-candidate"
72 1068639f Michael Hanselmann
NODE_ROLE_MASTER = "master"
73 1068639f Michael Hanselmann
NODE_ROLE_OFFLINE = "offline"
74 1068639f Michael Hanselmann
NODE_ROLE_REGULAR = "regular"
75 95ab4de9 David Knowles
76 63d5eb8a Michael Hanselmann
JOB_STATUS_QUEUED = "queued"
77 47099cd1 Michael Hanselmann
JOB_STATUS_WAITING = "waiting"
78 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELING = "canceling"
79 63d5eb8a Michael Hanselmann
JOB_STATUS_RUNNING = "running"
80 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELED = "canceled"
81 63d5eb8a Michael Hanselmann
JOB_STATUS_SUCCESS = "success"
82 63d5eb8a Michael Hanselmann
JOB_STATUS_ERROR = "error"
83 63d5eb8a Michael Hanselmann
JOB_STATUS_FINALIZED = frozenset([
84 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELED,
85 63d5eb8a Michael Hanselmann
  JOB_STATUS_SUCCESS,
86 63d5eb8a Michael Hanselmann
  JOB_STATUS_ERROR,
87 63d5eb8a Michael Hanselmann
  ])
88 63d5eb8a Michael Hanselmann
JOB_STATUS_ALL = frozenset([
89 63d5eb8a Michael Hanselmann
  JOB_STATUS_QUEUED,
90 47099cd1 Michael Hanselmann
  JOB_STATUS_WAITING,
91 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELING,
92 63d5eb8a Michael Hanselmann
  JOB_STATUS_RUNNING,
93 63d5eb8a Michael Hanselmann
  ]) | JOB_STATUS_FINALIZED
94 63d5eb8a Michael Hanselmann
95 47099cd1 Michael Hanselmann
# Legacy name
96 47099cd1 Michael Hanselmann
JOB_STATUS_WAITLOCK = JOB_STATUS_WAITING
97 47099cd1 Michael Hanselmann
98 8a47b447 Michael Hanselmann
# Internal constants
99 8a47b447 Michael Hanselmann
_REQ_DATA_VERSION_FIELD = "__version__"
100 c97fcab8 Guido Trotter
_INST_NIC_PARAMS = frozenset(["mac", "ip", "mode", "link"])
101 48436b97 Michael Hanselmann
_INST_CREATE_V0_DISK_PARAMS = frozenset(["size"])
102 48436b97 Michael Hanselmann
_INST_CREATE_V0_PARAMS = frozenset([
103 48436b97 Michael Hanselmann
  "os", "pnode", "snode", "iallocator", "start", "ip_check", "name_check",
104 48436b97 Michael Hanselmann
  "hypervisor", "file_storage_dir", "file_driver", "dry_run",
105 48436b97 Michael Hanselmann
  ])
106 48436b97 Michael Hanselmann
_INST_CREATE_V0_DPARAMS = frozenset(["beparams", "hvparams"])
107 4c864b55 Michael Hanselmann
_QPARAM_DRY_RUN = "dry-run"
108 4c864b55 Michael Hanselmann
_QPARAM_FORCE = "force"
109 8a47b447 Michael Hanselmann
110 6396dc04 Michael Hanselmann
# Feature strings
111 6396dc04 Michael Hanselmann
INST_CREATE_REQV1 = "instance-create-reqv1"
112 6396dc04 Michael Hanselmann
INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
113 6396dc04 Michael Hanselmann
NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
114 6396dc04 Michael Hanselmann
NODE_EVAC_RES1 = "node-evac-res1"
115 6396dc04 Michael Hanselmann
116 6396dc04 Michael Hanselmann
# Old feature constant names in case they're references by users of this module
117 6396dc04 Michael Hanselmann
_INST_CREATE_REQV1 = INST_CREATE_REQV1
118 6396dc04 Michael Hanselmann
_INST_REINSTALL_REQV1 = INST_REINSTALL_REQV1
119 6396dc04 Michael Hanselmann
_NODE_MIGRATE_REQV1 = NODE_MIGRATE_REQV1
120 6396dc04 Michael Hanselmann
_NODE_EVAC_RES1 = NODE_EVAC_RES1
121 6396dc04 Michael Hanselmann
122 2a7c3583 Michael Hanselmann
# Older pycURL versions don't have all error constants
123 2a7c3583 Michael Hanselmann
try:
124 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
125 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
126 2a7c3583 Michael Hanselmann
except AttributeError:
127 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = 60
128 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = 77
129 2a7c3583 Michael Hanselmann
130 2a7c3583 Michael Hanselmann
_CURL_SSL_CERT_ERRORS = frozenset([
131 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT,
132 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE,
133 2a7c3583 Michael Hanselmann
  ])
134 2a7c3583 Michael Hanselmann
135 95ab4de9 David Knowles
136 95ab4de9 David Knowles
class Error(Exception):
137 95ab4de9 David Knowles
  """Base error class for this module.
138 95ab4de9 David Knowles

139 95ab4de9 David Knowles
  """
140 95ab4de9 David Knowles
  pass
141 95ab4de9 David Knowles
142 95ab4de9 David Knowles
143 95ab4de9 David Knowles
class CertificateError(Error):
144 95ab4de9 David Knowles
  """Raised when a problem is found with the SSL certificate.
145 95ab4de9 David Knowles

146 95ab4de9 David Knowles
  """
147 95ab4de9 David Knowles
  pass
148 95ab4de9 David Knowles
149 95ab4de9 David Knowles
150 95ab4de9 David Knowles
class GanetiApiError(Error):
151 95ab4de9 David Knowles
  """Generic error raised from Ganeti API.
152 95ab4de9 David Knowles

153 95ab4de9 David Knowles
  """
154 8a019a03 Michael Hanselmann
  def __init__(self, msg, code=None):
155 8a019a03 Michael Hanselmann
    Error.__init__(self, msg)
156 8a019a03 Michael Hanselmann
    self.code = code
157 95ab4de9 David Knowles
158 95ab4de9 David Knowles
159 4c864b55 Michael Hanselmann
def _AppendIf(container, condition, value):
160 4c864b55 Michael Hanselmann
  """Appends to a list if a condition evaluates to truth.
161 4c864b55 Michael Hanselmann

162 4c864b55 Michael Hanselmann
  """
163 4c864b55 Michael Hanselmann
  if condition:
164 4c864b55 Michael Hanselmann
    container.append(value)
165 4c864b55 Michael Hanselmann
166 4c864b55 Michael Hanselmann
  return condition
167 4c864b55 Michael Hanselmann
168 4c864b55 Michael Hanselmann
169 4c864b55 Michael Hanselmann
def _AppendDryRunIf(container, condition):
170 4c864b55 Michael Hanselmann
  """Appends a "dry-run" parameter if a condition evaluates to truth.
171 4c864b55 Michael Hanselmann

172 4c864b55 Michael Hanselmann
  """
173 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_DRY_RUN, 1))
174 4c864b55 Michael Hanselmann
175 4c864b55 Michael Hanselmann
176 4c864b55 Michael Hanselmann
def _AppendForceIf(container, condition):
177 4c864b55 Michael Hanselmann
  """Appends a "force" parameter if a condition evaluates to truth.
178 4c864b55 Michael Hanselmann

179 4c864b55 Michael Hanselmann
  """
180 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_FORCE, 1))
181 4c864b55 Michael Hanselmann
182 4c864b55 Michael Hanselmann
183 57d8e228 Michael Hanselmann
def _SetItemIf(container, condition, item, value):
184 57d8e228 Michael Hanselmann
  """Sets an item if a condition evaluates to truth.
185 57d8e228 Michael Hanselmann

186 57d8e228 Michael Hanselmann
  """
187 57d8e228 Michael Hanselmann
  if condition:
188 57d8e228 Michael Hanselmann
    container[item] = value
189 57d8e228 Michael Hanselmann
190 57d8e228 Michael Hanselmann
  return condition
191 57d8e228 Michael Hanselmann
192 57d8e228 Michael Hanselmann
193 2a7c3583 Michael Hanselmann
def UsesRapiClient(fn):
194 2a7c3583 Michael Hanselmann
  """Decorator for code using RAPI client to initialize pycURL.
195 9279e986 Michael Hanselmann

196 9279e986 Michael Hanselmann
  """
197 2a7c3583 Michael Hanselmann
  def wrapper(*args, **kwargs):
198 2a7c3583 Michael Hanselmann
    # curl_global_init(3) and curl_global_cleanup(3) must be called with only
199 2a7c3583 Michael Hanselmann
    # one thread running. This check is just a safety measure -- it doesn't
200 2a7c3583 Michael Hanselmann
    # cover all cases.
201 2a7c3583 Michael Hanselmann
    assert threading.activeCount() == 1, \
202 2a7c3583 Michael Hanselmann
           "Found active threads when initializing pycURL"
203 2a7c3583 Michael Hanselmann
204 2a7c3583 Michael Hanselmann
    pycurl.global_init(pycurl.GLOBAL_ALL)
205 2a7c3583 Michael Hanselmann
    try:
206 2a7c3583 Michael Hanselmann
      return fn(*args, **kwargs)
207 2a7c3583 Michael Hanselmann
    finally:
208 2a7c3583 Michael Hanselmann
      pycurl.global_cleanup()
209 2a7c3583 Michael Hanselmann
210 2a7c3583 Michael Hanselmann
  return wrapper
211 2a7c3583 Michael Hanselmann
212 2a7c3583 Michael Hanselmann
213 2a7c3583 Michael Hanselmann
def GenericCurlConfig(verbose=False, use_signal=False,
214 2a7c3583 Michael Hanselmann
                      use_curl_cabundle=False, cafile=None, capath=None,
215 2a7c3583 Michael Hanselmann
                      proxy=None, verify_hostname=False,
216 2a7c3583 Michael Hanselmann
                      connect_timeout=None, timeout=None,
217 2a7c3583 Michael Hanselmann
                      _pycurl_version_fn=pycurl.version_info):
218 2a7c3583 Michael Hanselmann
  """Curl configuration function generator.
219 2a7c3583 Michael Hanselmann

220 2a7c3583 Michael Hanselmann
  @type verbose: bool
221 2a7c3583 Michael Hanselmann
  @param verbose: Whether to set cURL to verbose mode
222 2a7c3583 Michael Hanselmann
  @type use_signal: bool
223 2a7c3583 Michael Hanselmann
  @param use_signal: Whether to allow cURL to use signals
224 2a7c3583 Michael Hanselmann
  @type use_curl_cabundle: bool
225 2a7c3583 Michael Hanselmann
  @param use_curl_cabundle: Whether to use cURL's default CA bundle
226 2a7c3583 Michael Hanselmann
  @type cafile: string
227 2a7c3583 Michael Hanselmann
  @param cafile: In which file we can find the certificates
228 2a7c3583 Michael Hanselmann
  @type capath: string
229 2a7c3583 Michael Hanselmann
  @param capath: In which directory we can find the certificates
230 2a7c3583 Michael Hanselmann
  @type proxy: string
231 2a7c3583 Michael Hanselmann
  @param proxy: Proxy to use, None for default behaviour and empty string for
232 2a7c3583 Michael Hanselmann
                disabling proxies (see curl_easy_setopt(3))
233 2a7c3583 Michael Hanselmann
  @type verify_hostname: bool
234 2a7c3583 Michael Hanselmann
  @param verify_hostname: Whether to verify the remote peer certificate's
235 2a7c3583 Michael Hanselmann
                          commonName
236 2a7c3583 Michael Hanselmann
  @type connect_timeout: number
237 2a7c3583 Michael Hanselmann
  @param connect_timeout: Timeout for establishing connection in seconds
238 2a7c3583 Michael Hanselmann
  @type timeout: number
239 2a7c3583 Michael Hanselmann
  @param timeout: Timeout for complete transfer in seconds (see
240 2a7c3583 Michael Hanselmann
                  curl_easy_setopt(3)).
241 9279e986 Michael Hanselmann

242 9279e986 Michael Hanselmann
  """
243 2a7c3583 Michael Hanselmann
  if use_curl_cabundle and (cafile or capath):
244 2a7c3583 Michael Hanselmann
    raise Error("Can not use default CA bundle when CA file or path is set")
245 9279e986 Michael Hanselmann
246 2a7c3583 Michael Hanselmann
  def _ConfigCurl(curl, logger):
247 2a7c3583 Michael Hanselmann
    """Configures a cURL object
248 9279e986 Michael Hanselmann

249 2a7c3583 Michael Hanselmann
    @type curl: pycurl.Curl
250 2a7c3583 Michael Hanselmann
    @param curl: cURL object
251 9279e986 Michael Hanselmann

252 9279e986 Michael Hanselmann
    """
253 2a7c3583 Michael Hanselmann
    logger.debug("Using cURL version %s", pycurl.version)
254 2a7c3583 Michael Hanselmann
255 2a7c3583 Michael Hanselmann
    # pycurl.version_info returns a tuple with information about the used
256 2a7c3583 Michael Hanselmann
    # version of libcurl. Item 5 is the SSL library linked to it.
257 2a7c3583 Michael Hanselmann
    # e.g.: (3, '7.18.0', 463360, 'x86_64-pc-linux-gnu', 1581, 'GnuTLS/2.0.4',
258 2a7c3583 Michael Hanselmann
    # 0, '1.2.3.3', ...)
259 2a7c3583 Michael Hanselmann
    sslver = _pycurl_version_fn()[5]
260 2a7c3583 Michael Hanselmann
    if not sslver:
261 2a7c3583 Michael Hanselmann
      raise Error("No SSL support in cURL")
262 2a7c3583 Michael Hanselmann
263 2a7c3583 Michael Hanselmann
    lcsslver = sslver.lower()
264 2a7c3583 Michael Hanselmann
    if lcsslver.startswith("openssl/"):
265 2a7c3583 Michael Hanselmann
      pass
266 2a7c3583 Michael Hanselmann
    elif lcsslver.startswith("gnutls/"):
267 2a7c3583 Michael Hanselmann
      if capath:
268 2a7c3583 Michael Hanselmann
        raise Error("cURL linked against GnuTLS has no support for a"
269 2a7c3583 Michael Hanselmann
                    " CA path (%s)" % (pycurl.version, ))
270 9279e986 Michael Hanselmann
    else:
271 2a7c3583 Michael Hanselmann
      raise NotImplementedError("cURL uses unsupported SSL version '%s'" %
272 2a7c3583 Michael Hanselmann
                                sslver)
273 2a7c3583 Michael Hanselmann
274 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, verbose)
275 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, not use_signal)
276 2a7c3583 Michael Hanselmann
277 2a7c3583 Michael Hanselmann
    # Whether to verify remote peer's CN
278 2a7c3583 Michael Hanselmann
    if verify_hostname:
279 2a7c3583 Michael Hanselmann
      # curl_easy_setopt(3): "When CURLOPT_SSL_VERIFYHOST is 2, that
280 2a7c3583 Michael Hanselmann
      # certificate must indicate that the server is the server to which you
281 2a7c3583 Michael Hanselmann
      # meant to connect, or the connection fails. [...] When the value is 1,
282 2a7c3583 Michael Hanselmann
      # the certificate must contain a Common Name field, but it doesn't matter
283 2a7c3583 Michael Hanselmann
      # what name it says. [...]"
284 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 2)
285 beba56ae Michael Hanselmann
    else:
286 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 0)
287 2a7c3583 Michael Hanselmann
288 2a7c3583 Michael Hanselmann
    if cafile or capath or use_curl_cabundle:
289 2a7c3583 Michael Hanselmann
      # Require certificates to be checked
290 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, True)
291 2a7c3583 Michael Hanselmann
      if cafile:
292 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAINFO, str(cafile))
293 2a7c3583 Michael Hanselmann
      if capath:
294 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAPATH, str(capath))
295 2a7c3583 Michael Hanselmann
      # Not changing anything for using default CA bundle
296 2a7c3583 Michael Hanselmann
    else:
297 2a7c3583 Michael Hanselmann
      # Disable SSL certificate verification
298 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, False)
299 9279e986 Michael Hanselmann
300 2a7c3583 Michael Hanselmann
    if proxy is not None:
301 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.PROXY, str(proxy))
302 9279e986 Michael Hanselmann
303 2a7c3583 Michael Hanselmann
    # Timeouts
304 2a7c3583 Michael Hanselmann
    if connect_timeout is not None:
305 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.CONNECTTIMEOUT, connect_timeout)
306 2a7c3583 Michael Hanselmann
    if timeout is not None:
307 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.TIMEOUT, timeout)
308 9279e986 Michael Hanselmann
309 2a7c3583 Michael Hanselmann
  return _ConfigCurl
310 9279e986 Michael Hanselmann
311 9279e986 Michael Hanselmann
312 b459a848 Andrea Spadaccini
class GanetiRapiClient(object): # pylint: disable=R0904
313 95ab4de9 David Knowles
  """Ganeti RAPI client.
314 95ab4de9 David Knowles

315 95ab4de9 David Knowles
  """
316 95ab4de9 David Knowles
  USER_AGENT = "Ganeti RAPI Client"
317 d3844674 Michael Hanselmann
  _json_encoder = simplejson.JSONEncoder(sort_keys=True)
318 95ab4de9 David Knowles
319 9279e986 Michael Hanselmann
  def __init__(self, host, port=GANETI_RAPI_PORT,
320 2a7c3583 Michael Hanselmann
               username=None, password=None, logger=logging,
321 a5eba783 Michael Hanselmann
               curl_config_fn=None, curl_factory=None):
322 2a7c3583 Michael Hanselmann
    """Initializes this class.
323 95ab4de9 David Knowles

324 9279e986 Michael Hanselmann
    @type host: string
325 9279e986 Michael Hanselmann
    @param host: the ganeti cluster master to interact with
326 95ab4de9 David Knowles
    @type port: int
327 9279e986 Michael Hanselmann
    @param port: the port on which the RAPI is running (default is 5080)
328 9279e986 Michael Hanselmann
    @type username: string
329 95ab4de9 David Knowles
    @param username: the username to connect with
330 9279e986 Michael Hanselmann
    @type password: string
331 95ab4de9 David Knowles
    @param password: the password to connect with
332 2a7c3583 Michael Hanselmann
    @type curl_config_fn: callable
333 2a7c3583 Michael Hanselmann
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
334 9279e986 Michael Hanselmann
    @param logger: Logging object
335 95ab4de9 David Knowles

336 95ab4de9 David Knowles
    """
337 a5eba783 Michael Hanselmann
    self._username = username
338 a5eba783 Michael Hanselmann
    self._password = password
339 9279e986 Michael Hanselmann
    self._logger = logger
340 a5eba783 Michael Hanselmann
    self._curl_config_fn = curl_config_fn
341 a5eba783 Michael Hanselmann
    self._curl_factory = curl_factory
342 95ab4de9 David Knowles
343 1a8337f2 Manuel Franceschini
    try:
344 1a8337f2 Manuel Franceschini
      socket.inet_pton(socket.AF_INET6, host)
345 1a8337f2 Manuel Franceschini
      address = "[%s]:%s" % (host, port)
346 1a8337f2 Manuel Franceschini
    except socket.error:
347 1a8337f2 Manuel Franceschini
      address = "%s:%s" % (host, port)
348 1a8337f2 Manuel Franceschini
349 1a8337f2 Manuel Franceschini
    self._base_url = "https://%s" % address
350 f2f88abf David Knowles
351 a5eba783 Michael Hanselmann
    if username is not None:
352 a5eba783 Michael Hanselmann
      if password is None:
353 a5eba783 Michael Hanselmann
        raise Error("Password not specified")
354 a5eba783 Michael Hanselmann
    elif password:
355 a5eba783 Michael Hanselmann
      raise Error("Specified password without username")
356 a5eba783 Michael Hanselmann
357 a5eba783 Michael Hanselmann
  def _CreateCurl(self):
358 a5eba783 Michael Hanselmann
    """Creates a cURL object.
359 a5eba783 Michael Hanselmann

360 a5eba783 Michael Hanselmann
    """
361 a5eba783 Michael Hanselmann
    # Create pycURL object if no factory is provided
362 a5eba783 Michael Hanselmann
    if self._curl_factory:
363 a5eba783 Michael Hanselmann
      curl = self._curl_factory()
364 a5eba783 Michael Hanselmann
    else:
365 2a7c3583 Michael Hanselmann
      curl = pycurl.Curl()
366 2a7c3583 Michael Hanselmann
367 2a7c3583 Michael Hanselmann
    # Default cURL settings
368 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, False)
369 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.FOLLOWLOCATION, False)
370 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.MAXREDIRS, 5)
371 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, True)
372 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.USERAGENT, self.USER_AGENT)
373 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYHOST, 0)
374 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYPEER, False)
375 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.HTTPHEADER, [
376 2a7c3583 Michael Hanselmann
      "Accept: %s" % HTTP_APP_JSON,
377 2a7c3583 Michael Hanselmann
      "Content-type: %s" % HTTP_APP_JSON,
378 2a7c3583 Michael Hanselmann
      ])
379 2a7c3583 Michael Hanselmann
380 a5eba783 Michael Hanselmann
    assert ((self._username is None and self._password is None) ^
381 a5eba783 Michael Hanselmann
            (self._username is not None and self._password is not None))
382 a5eba783 Michael Hanselmann
383 a5eba783 Michael Hanselmann
    if self._username:
384 a5eba783 Michael Hanselmann
      # Setup authentication
385 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
386 a5eba783 Michael Hanselmann
      curl.setopt(pycurl.USERPWD,
387 a5eba783 Michael Hanselmann
                  str("%s:%s" % (self._username, self._password)))
388 9279e986 Michael Hanselmann
389 2a7c3583 Michael Hanselmann
    # Call external configuration function
390 a5eba783 Michael Hanselmann
    if self._curl_config_fn:
391 a5eba783 Michael Hanselmann
      self._curl_config_fn(curl, self._logger)
392 f2f88abf David Knowles
393 a5eba783 Michael Hanselmann
    return curl
394 95ab4de9 David Knowles
395 10f5ab6c Michael Hanselmann
  @staticmethod
396 10f5ab6c Michael Hanselmann
  def _EncodeQuery(query):
397 10f5ab6c Michael Hanselmann
    """Encode query values for RAPI URL.
398 10f5ab6c Michael Hanselmann

399 10f5ab6c Michael Hanselmann
    @type query: list of two-tuples
400 10f5ab6c Michael Hanselmann
    @param query: Query arguments
401 10f5ab6c Michael Hanselmann
    @rtype: list
402 10f5ab6c Michael Hanselmann
    @return: Query list with encoded values
403 10f5ab6c Michael Hanselmann

404 10f5ab6c Michael Hanselmann
    """
405 10f5ab6c Michael Hanselmann
    result = []
406 10f5ab6c Michael Hanselmann
407 10f5ab6c Michael Hanselmann
    for name, value in query:
408 10f5ab6c Michael Hanselmann
      if value is None:
409 10f5ab6c Michael Hanselmann
        result.append((name, ""))
410 10f5ab6c Michael Hanselmann
411 10f5ab6c Michael Hanselmann
      elif isinstance(value, bool):
412 10f5ab6c Michael Hanselmann
        # Boolean values must be encoded as 0 or 1
413 10f5ab6c Michael Hanselmann
        result.append((name, int(value)))
414 10f5ab6c Michael Hanselmann
415 10f5ab6c Michael Hanselmann
      elif isinstance(value, (list, tuple, dict)):
416 10f5ab6c Michael Hanselmann
        raise ValueError("Invalid query data type %r" % type(value).__name__)
417 10f5ab6c Michael Hanselmann
418 10f5ab6c Michael Hanselmann
      else:
419 10f5ab6c Michael Hanselmann
        result.append((name, value))
420 10f5ab6c Michael Hanselmann
421 10f5ab6c Michael Hanselmann
    return result
422 10f5ab6c Michael Hanselmann
423 768747ed Michael Hanselmann
  def _SendRequest(self, method, path, query, content):
424 95ab4de9 David Knowles
    """Sends an HTTP request.
425 95ab4de9 David Knowles

426 95ab4de9 David Knowles
    This constructs a full URL, encodes and decodes HTTP bodies, and
427 95ab4de9 David Knowles
    handles invalid responses in a pythonic way.
428 95ab4de9 David Knowles

429 768747ed Michael Hanselmann
    @type method: string
430 95ab4de9 David Knowles
    @param method: HTTP method to use
431 768747ed Michael Hanselmann
    @type path: string
432 95ab4de9 David Knowles
    @param path: HTTP URL path
433 95ab4de9 David Knowles
    @type query: list of two-tuples
434 95ab4de9 David Knowles
    @param query: query arguments to pass to urllib.urlencode
435 95ab4de9 David Knowles
    @type content: str or None
436 95ab4de9 David Knowles
    @param content: HTTP body content
437 95ab4de9 David Knowles

438 95ab4de9 David Knowles
    @rtype: str
439 95ab4de9 David Knowles
    @return: JSON-Decoded response
440 95ab4de9 David Knowles

441 f2f88abf David Knowles
    @raises CertificateError: If an invalid SSL certificate is found
442 95ab4de9 David Knowles
    @raises GanetiApiError: If an invalid response is returned
443 95ab4de9 David Knowles

444 95ab4de9 David Knowles
    """
445 ccd6b542 Michael Hanselmann
    assert path.startswith("/")
446 ccd6b542 Michael Hanselmann
447 a5eba783 Michael Hanselmann
    curl = self._CreateCurl()
448 2a7c3583 Michael Hanselmann
449 8306e0e4 Michael Hanselmann
    if content is not None:
450 d3844674 Michael Hanselmann
      encoded_content = self._json_encoder.encode(content)
451 d3844674 Michael Hanselmann
    else:
452 2a7c3583 Michael Hanselmann
      encoded_content = ""
453 95ab4de9 David Knowles
454 ccd6b542 Michael Hanselmann
    # Build URL
455 f961e2ba Michael Hanselmann
    urlparts = [self._base_url, path]
456 ccd6b542 Michael Hanselmann
    if query:
457 f961e2ba Michael Hanselmann
      urlparts.append("?")
458 f961e2ba Michael Hanselmann
      urlparts.append(urllib.urlencode(self._EncodeQuery(query)))
459 9279e986 Michael Hanselmann
460 f961e2ba Michael Hanselmann
    url = "".join(urlparts)
461 f961e2ba Michael Hanselmann
462 a5eba783 Michael Hanselmann
    self._logger.debug("Sending request %s %s (content=%r)",
463 a5eba783 Michael Hanselmann
                       method, url, encoded_content)
464 2a7c3583 Michael Hanselmann
465 2a7c3583 Michael Hanselmann
    # Buffer for response
466 2a7c3583 Michael Hanselmann
    encoded_resp_body = StringIO()
467 f961e2ba Michael Hanselmann
468 2a7c3583 Michael Hanselmann
    # Configure cURL
469 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.CUSTOMREQUEST, str(method))
470 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.URL, str(url))
471 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.POSTFIELDS, str(encoded_content))
472 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.WRITEFUNCTION, encoded_resp_body.write)
473 9279e986 Michael Hanselmann
474 f2f88abf David Knowles
    try:
475 2a7c3583 Michael Hanselmann
      # Send request and wait for response
476 2a7c3583 Michael Hanselmann
      try:
477 2a7c3583 Michael Hanselmann
        curl.perform()
478 2a7c3583 Michael Hanselmann
      except pycurl.error, err:
479 2a7c3583 Michael Hanselmann
        if err.args[0] in _CURL_SSL_CERT_ERRORS:
480 2a7c3583 Michael Hanselmann
          raise CertificateError("SSL certificate error %s" % err)
481 2a7c3583 Michael Hanselmann
482 2a7c3583 Michael Hanselmann
        raise GanetiApiError(str(err))
483 2a7c3583 Michael Hanselmann
    finally:
484 2a7c3583 Michael Hanselmann
      # Reset settings to not keep references to large objects in memory
485 2a7c3583 Michael Hanselmann
      # between requests
486 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.POSTFIELDS, "")
487 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.WRITEFUNCTION, lambda _: None)
488 2a7c3583 Michael Hanselmann
489 2a7c3583 Michael Hanselmann
    # Get HTTP response code
490 2a7c3583 Michael Hanselmann
    http_code = curl.getinfo(pycurl.RESPONSE_CODE)
491 2a7c3583 Michael Hanselmann
492 2a7c3583 Michael Hanselmann
    # Was anything written to the response buffer?
493 2a7c3583 Michael Hanselmann
    if encoded_resp_body.tell():
494 2a7c3583 Michael Hanselmann
      response_content = simplejson.loads(encoded_resp_body.getvalue())
495 d3844674 Michael Hanselmann
    else:
496 d3844674 Michael Hanselmann
      response_content = None
497 95ab4de9 David Knowles
498 2a7c3583 Michael Hanselmann
    if http_code != HTTP_OK:
499 d3844674 Michael Hanselmann
      if isinstance(response_content, dict):
500 95ab4de9 David Knowles
        msg = ("%s %s: %s" %
501 d3844674 Michael Hanselmann
               (response_content["code"],
502 d3844674 Michael Hanselmann
                response_content["message"],
503 d3844674 Michael Hanselmann
                response_content["explain"]))
504 95ab4de9 David Knowles
      else:
505 d3844674 Michael Hanselmann
        msg = str(response_content)
506 d3844674 Michael Hanselmann
507 2a7c3583 Michael Hanselmann
      raise GanetiApiError(msg, code=http_code)
508 95ab4de9 David Knowles
509 d3844674 Michael Hanselmann
    return response_content
510 95ab4de9 David Knowles
511 95ab4de9 David Knowles
  def GetVersion(self):
512 cab667cc David Knowles
    """Gets the Remote API version running on the cluster.
513 95ab4de9 David Knowles

514 95ab4de9 David Knowles
    @rtype: int
515 f2f88abf David Knowles
    @return: Ganeti Remote API version
516 95ab4de9 David Knowles

517 95ab4de9 David Knowles
    """
518 768747ed Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/version", None, None)
519 95ab4de9 David Knowles
520 7eac4a4d Michael Hanselmann
  def GetFeatures(self):
521 7eac4a4d Michael Hanselmann
    """Gets the list of optional features supported by RAPI server.
522 7eac4a4d Michael Hanselmann

523 7eac4a4d Michael Hanselmann
    @rtype: list
524 7eac4a4d Michael Hanselmann
    @return: List of optional features
525 7eac4a4d Michael Hanselmann

526 7eac4a4d Michael Hanselmann
    """
527 7eac4a4d Michael Hanselmann
    try:
528 7eac4a4d Michael Hanselmann
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
529 7eac4a4d Michael Hanselmann
                               None, None)
530 7eac4a4d Michael Hanselmann
    except GanetiApiError, err:
531 7eac4a4d Michael Hanselmann
      # Older RAPI servers don't support this resource
532 7eac4a4d Michael Hanselmann
      if err.code == HTTP_NOT_FOUND:
533 7eac4a4d Michael Hanselmann
        return []
534 7eac4a4d Michael Hanselmann
535 7eac4a4d Michael Hanselmann
      raise
536 7eac4a4d Michael Hanselmann
537 95ab4de9 David Knowles
  def GetOperatingSystems(self):
538 95ab4de9 David Knowles
    """Gets the Operating Systems running in the Ganeti cluster.
539 95ab4de9 David Knowles

540 95ab4de9 David Knowles
    @rtype: list of str
541 95ab4de9 David Knowles
    @return: operating systems
542 95ab4de9 David Knowles

543 95ab4de9 David Knowles
    """
544 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
545 a198b2d9 Michael Hanselmann
                             None, None)
546 95ab4de9 David Knowles
547 95ab4de9 David Knowles
  def GetInfo(self):
548 95ab4de9 David Knowles
    """Gets info about the cluster.
549 95ab4de9 David Knowles

550 95ab4de9 David Knowles
    @rtype: dict
551 95ab4de9 David Knowles
    @return: information about the cluster
552 95ab4de9 David Knowles

553 95ab4de9 David Knowles
    """
554 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
555 a198b2d9 Michael Hanselmann
                             None, None)
556 95ab4de9 David Knowles
557 54d4c13b Michael Hanselmann
  def RedistributeConfig(self):
558 54d4c13b Michael Hanselmann
    """Tells the cluster to redistribute its configuration files.
559 54d4c13b Michael Hanselmann

560 d914c76f Simeon Miteff
    @rtype: string
561 54d4c13b Michael Hanselmann
    @return: job id
562 54d4c13b Michael Hanselmann

563 54d4c13b Michael Hanselmann
    """
564 54d4c13b Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
565 54d4c13b Michael Hanselmann
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
566 54d4c13b Michael Hanselmann
                             None, None)
567 54d4c13b Michael Hanselmann
568 62e999a5 Michael Hanselmann
  def ModifyCluster(self, **kwargs):
569 62e999a5 Michael Hanselmann
    """Modifies cluster parameters.
570 62e999a5 Michael Hanselmann

571 62e999a5 Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
572 62e999a5 Michael Hanselmann

573 98805538 Michael Hanselmann
    @rtype: string
574 62e999a5 Michael Hanselmann
    @return: job id
575 62e999a5 Michael Hanselmann

576 62e999a5 Michael Hanselmann
    """
577 62e999a5 Michael Hanselmann
    body = kwargs
578 62e999a5 Michael Hanselmann
579 62e999a5 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
580 62e999a5 Michael Hanselmann
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
581 62e999a5 Michael Hanselmann
582 95ab4de9 David Knowles
  def GetClusterTags(self):
583 95ab4de9 David Knowles
    """Gets the cluster tags.
584 95ab4de9 David Knowles

585 95ab4de9 David Knowles
    @rtype: list of str
586 95ab4de9 David Knowles
    @return: cluster tags
587 95ab4de9 David Knowles

588 95ab4de9 David Knowles
    """
589 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
590 a198b2d9 Michael Hanselmann
                             None, None)
591 95ab4de9 David Knowles
592 95ab4de9 David Knowles
  def AddClusterTags(self, tags, dry_run=False):
593 95ab4de9 David Knowles
    """Adds tags to the cluster.
594 95ab4de9 David Knowles

595 95ab4de9 David Knowles
    @type tags: list of str
596 95ab4de9 David Knowles
    @param tags: tags to add to the cluster
597 95ab4de9 David Knowles
    @type dry_run: bool
598 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
599 95ab4de9 David Knowles

600 98805538 Michael Hanselmann
    @rtype: string
601 95ab4de9 David Knowles
    @return: job id
602 95ab4de9 David Knowles

603 95ab4de9 David Knowles
    """
604 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
605 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
606 95ab4de9 David Knowles
607 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
608 a198b2d9 Michael Hanselmann
                             query, None)
609 95ab4de9 David Knowles
610 95ab4de9 David Knowles
  def DeleteClusterTags(self, tags, dry_run=False):
611 95ab4de9 David Knowles
    """Deletes tags from the cluster.
612 95ab4de9 David Knowles

613 95ab4de9 David Knowles
    @type tags: list of str
614 95ab4de9 David Knowles
    @param tags: tags to delete
615 95ab4de9 David Knowles
    @type dry_run: bool
616 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
617 d914c76f Simeon Miteff
    @rtype: string
618 d914c76f Simeon Miteff
    @return: job id
619 95ab4de9 David Knowles

620 95ab4de9 David Knowles
    """
621 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
622 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
623 95ab4de9 David Knowles
624 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
625 a198b2d9 Michael Hanselmann
                             query, None)
626 95ab4de9 David Knowles
627 95ab4de9 David Knowles
  def GetInstances(self, bulk=False):
628 95ab4de9 David Knowles
    """Gets information about instances on the cluster.
629 95ab4de9 David Knowles

630 95ab4de9 David Knowles
    @type bulk: bool
631 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
632 95ab4de9 David Knowles

633 95ab4de9 David Knowles
    @rtype: list of dict or list of str
634 95ab4de9 David Knowles
    @return: if bulk is True, info about the instances, else a list of instances
635 95ab4de9 David Knowles

636 95ab4de9 David Knowles
    """
637 95ab4de9 David Knowles
    query = []
638 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
639 95ab4de9 David Knowles
640 a198b2d9 Michael Hanselmann
    instances = self._SendRequest(HTTP_GET,
641 a198b2d9 Michael Hanselmann
                                  "/%s/instances" % GANETI_RAPI_VERSION,
642 a198b2d9 Michael Hanselmann
                                  query, None)
643 95ab4de9 David Knowles
    if bulk:
644 95ab4de9 David Knowles
      return instances
645 95ab4de9 David Knowles
    else:
646 95ab4de9 David Knowles
      return [i["id"] for i in instances]
647 95ab4de9 David Knowles
648 591e5103 Michael Hanselmann
  def GetInstance(self, instance):
649 95ab4de9 David Knowles
    """Gets information about an instance.
650 95ab4de9 David Knowles

651 95ab4de9 David Knowles
    @type instance: str
652 95ab4de9 David Knowles
    @param instance: instance whose info to return
653 95ab4de9 David Knowles

654 95ab4de9 David Knowles
    @rtype: dict
655 95ab4de9 David Knowles
    @return: info about the instance
656 95ab4de9 David Knowles

657 95ab4de9 David Knowles
    """
658 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
659 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
660 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
661 95ab4de9 David Knowles
662 591e5103 Michael Hanselmann
  def GetInstanceInfo(self, instance, static=None):
663 591e5103 Michael Hanselmann
    """Gets information about an instance.
664 591e5103 Michael Hanselmann

665 591e5103 Michael Hanselmann
    @type instance: string
666 591e5103 Michael Hanselmann
    @param instance: Instance name
667 591e5103 Michael Hanselmann
    @rtype: string
668 591e5103 Michael Hanselmann
    @return: Job ID
669 591e5103 Michael Hanselmann

670 591e5103 Michael Hanselmann
    """
671 591e5103 Michael Hanselmann
    if static is not None:
672 591e5103 Michael Hanselmann
      query = [("static", static)]
673 591e5103 Michael Hanselmann
    else:
674 591e5103 Michael Hanselmann
      query = None
675 591e5103 Michael Hanselmann
676 591e5103 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
677 591e5103 Michael Hanselmann
                             ("/%s/instances/%s/info" %
678 591e5103 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
679 591e5103 Michael Hanselmann
680 8a47b447 Michael Hanselmann
  def CreateInstance(self, mode, name, disk_template, disks, nics,
681 8a47b447 Michael Hanselmann
                     **kwargs):
682 95ab4de9 David Knowles
    """Creates a new instance.
683 95ab4de9 David Knowles

684 8a47b447 Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
685 8a47b447 Michael Hanselmann

686 8a47b447 Michael Hanselmann
    @type mode: string
687 8a47b447 Michael Hanselmann
    @param mode: Instance creation mode
688 8a47b447 Michael Hanselmann
    @type name: string
689 8a47b447 Michael Hanselmann
    @param name: Hostname of the instance to create
690 8a47b447 Michael Hanselmann
    @type disk_template: string
691 8a47b447 Michael Hanselmann
    @param disk_template: Disk template for instance (e.g. plain, diskless,
692 8a47b447 Michael Hanselmann
                          file, or drbd)
693 8a47b447 Michael Hanselmann
    @type disks: list of dicts
694 8a47b447 Michael Hanselmann
    @param disks: List of disk definitions
695 8a47b447 Michael Hanselmann
    @type nics: list of dicts
696 8a47b447 Michael Hanselmann
    @param nics: List of NIC definitions
697 95ab4de9 David Knowles
    @type dry_run: bool
698 8a47b447 Michael Hanselmann
    @keyword dry_run: whether to perform a dry run
699 95ab4de9 David Knowles

700 98805538 Michael Hanselmann
    @rtype: string
701 95ab4de9 David Knowles
    @return: job id
702 95ab4de9 David Knowles

703 95ab4de9 David Knowles
    """
704 95ab4de9 David Knowles
    query = []
705 8a47b447 Michael Hanselmann
706 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, kwargs.get("dry_run"))
707 95ab4de9 David Knowles
708 8a47b447 Michael Hanselmann
    if _INST_CREATE_REQV1 in self.GetFeatures():
709 8a47b447 Michael Hanselmann
      # All required fields for request data version 1
710 8a47b447 Michael Hanselmann
      body = {
711 8a47b447 Michael Hanselmann
        _REQ_DATA_VERSION_FIELD: 1,
712 8a47b447 Michael Hanselmann
        "mode": mode,
713 8a47b447 Michael Hanselmann
        "name": name,
714 8a47b447 Michael Hanselmann
        "disk_template": disk_template,
715 8a47b447 Michael Hanselmann
        "disks": disks,
716 8a47b447 Michael Hanselmann
        "nics": nics,
717 8a47b447 Michael Hanselmann
        }
718 8a47b447 Michael Hanselmann
719 8a47b447 Michael Hanselmann
      conflicts = set(kwargs.iterkeys()) & set(body.iterkeys())
720 8a47b447 Michael Hanselmann
      if conflicts:
721 8a47b447 Michael Hanselmann
        raise GanetiApiError("Required fields can not be specified as"
722 8a47b447 Michael Hanselmann
                             " keywords: %s" % ", ".join(conflicts))
723 8a47b447 Michael Hanselmann
724 8a47b447 Michael Hanselmann
      body.update((key, value) for key, value in kwargs.iteritems()
725 8a47b447 Michael Hanselmann
                  if key != "dry_run")
726 8a47b447 Michael Hanselmann
    else:
727 9a8ae794 Michael Hanselmann
      raise GanetiApiError("Server does not support new-style (version 1)"
728 9a8ae794 Michael Hanselmann
                           " instance creation requests")
729 8a47b447 Michael Hanselmann
730 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
731 8a47b447 Michael Hanselmann
                             query, body)
732 95ab4de9 David Knowles
733 95ab4de9 David Knowles
  def DeleteInstance(self, instance, dry_run=False):
734 95ab4de9 David Knowles
    """Deletes an instance.
735 95ab4de9 David Knowles

736 95ab4de9 David Knowles
    @type instance: str
737 95ab4de9 David Knowles
    @param instance: the instance to delete
738 95ab4de9 David Knowles

739 98805538 Michael Hanselmann
    @rtype: string
740 cab667cc David Knowles
    @return: job id
741 cab667cc David Knowles

742 95ab4de9 David Knowles
    """
743 95ab4de9 David Knowles
    query = []
744 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
745 95ab4de9 David Knowles
746 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
747 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
748 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
749 95ab4de9 David Knowles
750 3b7158ef Michael Hanselmann
  def ModifyInstance(self, instance, **kwargs):
751 3b7158ef Michael Hanselmann
    """Modifies an instance.
752 3b7158ef Michael Hanselmann

753 3b7158ef Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
754 3b7158ef Michael Hanselmann

755 3b7158ef Michael Hanselmann
    @type instance: string
756 3b7158ef Michael Hanselmann
    @param instance: Instance name
757 98805538 Michael Hanselmann
    @rtype: string
758 3b7158ef Michael Hanselmann
    @return: job id
759 3b7158ef Michael Hanselmann

760 3b7158ef Michael Hanselmann
    """
761 3b7158ef Michael Hanselmann
    body = kwargs
762 3b7158ef Michael Hanselmann
763 3b7158ef Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
764 3b7158ef Michael Hanselmann
                             ("/%s/instances/%s/modify" %
765 3b7158ef Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
766 3b7158ef Michael Hanselmann
767 b680c8be Michael Hanselmann
  def ActivateInstanceDisks(self, instance, ignore_size=None):
768 b680c8be Michael Hanselmann
    """Activates an instance's disks.
769 b680c8be Michael Hanselmann

770 b680c8be Michael Hanselmann
    @type instance: string
771 b680c8be Michael Hanselmann
    @param instance: Instance name
772 b680c8be Michael Hanselmann
    @type ignore_size: bool
773 b680c8be Michael Hanselmann
    @param ignore_size: Whether to ignore recorded size
774 d914c76f Simeon Miteff
    @rtype: string
775 b680c8be Michael Hanselmann
    @return: job id
776 b680c8be Michael Hanselmann

777 b680c8be Michael Hanselmann
    """
778 b680c8be Michael Hanselmann
    query = []
779 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_size, ("ignore_size", 1))
780 b680c8be Michael Hanselmann
781 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
782 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/activate-disks" %
783 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
784 b680c8be Michael Hanselmann
785 b680c8be Michael Hanselmann
  def DeactivateInstanceDisks(self, instance):
786 b680c8be Michael Hanselmann
    """Deactivates an instance's disks.
787 b680c8be Michael Hanselmann

788 b680c8be Michael Hanselmann
    @type instance: string
789 b680c8be Michael Hanselmann
    @param instance: Instance name
790 d914c76f Simeon Miteff
    @rtype: string
791 b680c8be Michael Hanselmann
    @return: job id
792 b680c8be Michael Hanselmann

793 b680c8be Michael Hanselmann
    """
794 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
795 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/deactivate-disks" %
796 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
797 b680c8be Michael Hanselmann
798 a52978c7 Michael Hanselmann
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
799 a52978c7 Michael Hanselmann
    """Recreate an instance's disks.
800 a52978c7 Michael Hanselmann

801 a52978c7 Michael Hanselmann
    @type instance: string
802 a52978c7 Michael Hanselmann
    @param instance: Instance name
803 a52978c7 Michael Hanselmann
    @type disks: list of int
804 a52978c7 Michael Hanselmann
    @param disks: List of disk indexes
805 a52978c7 Michael Hanselmann
    @type nodes: list of string
806 a52978c7 Michael Hanselmann
    @param nodes: New instance nodes, if relocation is desired
807 a52978c7 Michael Hanselmann
    @rtype: string
808 a52978c7 Michael Hanselmann
    @return: job id
809 a52978c7 Michael Hanselmann

810 a52978c7 Michael Hanselmann
    """
811 a52978c7 Michael Hanselmann
    body = {}
812 57d8e228 Michael Hanselmann
    _SetItemIf(body, disks is not None, "disks", disks)
813 57d8e228 Michael Hanselmann
    _SetItemIf(body, nodes is not None, "nodes", nodes)
814 a52978c7 Michael Hanselmann
815 a52978c7 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
816 a52978c7 Michael Hanselmann
                             ("/%s/instances/%s/recreate-disks" %
817 a52978c7 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
818 a52978c7 Michael Hanselmann
819 e23881ed Michael Hanselmann
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
820 e23881ed Michael Hanselmann
    """Grows a disk of an instance.
821 e23881ed Michael Hanselmann

822 e23881ed Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
823 e23881ed Michael Hanselmann

824 e23881ed Michael Hanselmann
    @type instance: string
825 e23881ed Michael Hanselmann
    @param instance: Instance name
826 e23881ed Michael Hanselmann
    @type disk: integer
827 e23881ed Michael Hanselmann
    @param disk: Disk index
828 e23881ed Michael Hanselmann
    @type amount: integer
829 e23881ed Michael Hanselmann
    @param amount: Grow disk by this amount (MiB)
830 e23881ed Michael Hanselmann
    @type wait_for_sync: bool
831 e23881ed Michael Hanselmann
    @param wait_for_sync: Wait for disk to synchronize
832 98805538 Michael Hanselmann
    @rtype: string
833 e23881ed Michael Hanselmann
    @return: job id
834 e23881ed Michael Hanselmann

835 e23881ed Michael Hanselmann
    """
836 e23881ed Michael Hanselmann
    body = {
837 e23881ed Michael Hanselmann
      "amount": amount,
838 e23881ed Michael Hanselmann
      }
839 e23881ed Michael Hanselmann
840 57d8e228 Michael Hanselmann
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
841 e23881ed Michael Hanselmann
842 e23881ed Michael Hanselmann
    return self._SendRequest(HTTP_POST,
843 e23881ed Michael Hanselmann
                             ("/%s/instances/%s/disk/%s/grow" %
844 e23881ed Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance, disk)),
845 e23881ed Michael Hanselmann
                             None, body)
846 e23881ed Michael Hanselmann
847 95ab4de9 David Knowles
  def GetInstanceTags(self, instance):
848 95ab4de9 David Knowles
    """Gets tags for an instance.
849 95ab4de9 David Knowles

850 95ab4de9 David Knowles
    @type instance: str
851 95ab4de9 David Knowles
    @param instance: instance whose tags to return
852 95ab4de9 David Knowles

853 95ab4de9 David Knowles
    @rtype: list of str
854 95ab4de9 David Knowles
    @return: tags for the instance
855 95ab4de9 David Knowles

856 95ab4de9 David Knowles
    """
857 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
858 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
859 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
860 95ab4de9 David Knowles
861 95ab4de9 David Knowles
  def AddInstanceTags(self, instance, tags, dry_run=False):
862 95ab4de9 David Knowles
    """Adds tags to an instance.
863 95ab4de9 David Knowles

864 95ab4de9 David Knowles
    @type instance: str
865 95ab4de9 David Knowles
    @param instance: instance to add tags to
866 95ab4de9 David Knowles
    @type tags: list of str
867 95ab4de9 David Knowles
    @param tags: tags to add to the instance
868 95ab4de9 David Knowles
    @type dry_run: bool
869 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
870 95ab4de9 David Knowles

871 98805538 Michael Hanselmann
    @rtype: string
872 95ab4de9 David Knowles
    @return: job id
873 95ab4de9 David Knowles

874 95ab4de9 David Knowles
    """
875 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
876 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
877 95ab4de9 David Knowles
878 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
879 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
880 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
881 95ab4de9 David Knowles
882 95ab4de9 David Knowles
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
883 95ab4de9 David Knowles
    """Deletes tags from an instance.
884 95ab4de9 David Knowles

885 95ab4de9 David Knowles
    @type instance: str
886 95ab4de9 David Knowles
    @param instance: instance to delete tags from
887 95ab4de9 David Knowles
    @type tags: list of str
888 95ab4de9 David Knowles
    @param tags: tags to delete
889 95ab4de9 David Knowles
    @type dry_run: bool
890 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
891 d914c76f Simeon Miteff
    @rtype: string
892 d914c76f Simeon Miteff
    @return: job id
893 95ab4de9 David Knowles

894 95ab4de9 David Knowles
    """
895 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
896 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
897 95ab4de9 David Knowles
898 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
899 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
900 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
901 95ab4de9 David Knowles
902 95ab4de9 David Knowles
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
903 95ab4de9 David Knowles
                     dry_run=False):
904 95ab4de9 David Knowles
    """Reboots an instance.
905 95ab4de9 David Knowles

906 95ab4de9 David Knowles
    @type instance: str
907 95ab4de9 David Knowles
    @param instance: instance to rebot
908 95ab4de9 David Knowles
    @type reboot_type: str
909 95ab4de9 David Knowles
    @param reboot_type: one of: hard, soft, full
910 95ab4de9 David Knowles
    @type ignore_secondaries: bool
911 95ab4de9 David Knowles
    @param ignore_secondaries: if True, ignores errors for the secondary node
912 95ab4de9 David Knowles
        while re-assembling disks (in hard-reboot mode only)
913 95ab4de9 David Knowles
    @type dry_run: bool
914 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
915 d914c76f Simeon Miteff
    @rtype: string
916 d914c76f Simeon Miteff
    @return: job id
917 95ab4de9 David Knowles

918 95ab4de9 David Knowles
    """
919 95ab4de9 David Knowles
    query = []
920 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
921 4c864b55 Michael Hanselmann
    _AppendIf(query, reboot_type, ("type", reboot_type))
922 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_secondaries is not None,
923 4c864b55 Michael Hanselmann
              ("ignore_secondaries", ignore_secondaries))
924 95ab4de9 David Knowles
925 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
926 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reboot" %
927 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
928 95ab4de9 David Knowles
929 2ba39b8f Iustin Pop
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False):
930 95ab4de9 David Knowles
    """Shuts down an instance.
931 95ab4de9 David Knowles

932 95ab4de9 David Knowles
    @type instance: str
933 95ab4de9 David Knowles
    @param instance: the instance to shut down
934 95ab4de9 David Knowles
    @type dry_run: bool
935 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
936 2ba39b8f Iustin Pop
    @type no_remember: bool
937 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
938 d914c76f Simeon Miteff
    @rtype: string
939 d914c76f Simeon Miteff
    @return: job id
940 95ab4de9 David Knowles

941 95ab4de9 David Knowles
    """
942 95ab4de9 David Knowles
    query = []
943 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
944 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
945 95ab4de9 David Knowles
946 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
947 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/shutdown" %
948 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
949 95ab4de9 David Knowles
950 2ba39b8f Iustin Pop
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
951 95ab4de9 David Knowles
    """Starts up an instance.
952 95ab4de9 David Knowles

953 95ab4de9 David Knowles
    @type instance: str
954 95ab4de9 David Knowles
    @param instance: the instance to start up
955 95ab4de9 David Knowles
    @type dry_run: bool
956 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
957 2ba39b8f Iustin Pop
    @type no_remember: bool
958 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
959 d914c76f Simeon Miteff
    @rtype: string
960 d914c76f Simeon Miteff
    @return: job id
961 95ab4de9 David Knowles

962 95ab4de9 David Knowles
    """
963 95ab4de9 David Knowles
    query = []
964 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
965 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
966 95ab4de9 David Knowles
967 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
968 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/startup" %
969 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
970 95ab4de9 David Knowles
971 c744425f Michael Hanselmann
  def ReinstallInstance(self, instance, os=None, no_startup=False,
972 c744425f Michael Hanselmann
                        osparams=None):
973 95ab4de9 David Knowles
    """Reinstalls an instance.
974 95ab4de9 David Knowles

975 95ab4de9 David Knowles
    @type instance: str
976 fcee9675 David Knowles
    @param instance: The instance to reinstall
977 fcee9675 David Knowles
    @type os: str or None
978 fcee9675 David Knowles
    @param os: The operating system to reinstall. If None, the instance's
979 fcee9675 David Knowles
        current operating system will be installed again
980 95ab4de9 David Knowles
    @type no_startup: bool
981 fcee9675 David Knowles
    @param no_startup: Whether to start the instance automatically
982 d914c76f Simeon Miteff
    @rtype: string
983 d914c76f Simeon Miteff
    @return: job id
984 95ab4de9 David Knowles

985 95ab4de9 David Knowles
    """
986 c744425f Michael Hanselmann
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
987 c744425f Michael Hanselmann
      body = {
988 c744425f Michael Hanselmann
        "start": not no_startup,
989 c744425f Michael Hanselmann
        }
990 57d8e228 Michael Hanselmann
      _SetItemIf(body, os is not None, "os", os)
991 57d8e228 Michael Hanselmann
      _SetItemIf(body, osparams is not None, "osparams", osparams)
992 c744425f Michael Hanselmann
      return self._SendRequest(HTTP_POST,
993 c744425f Michael Hanselmann
                               ("/%s/instances/%s/reinstall" %
994 c744425f Michael Hanselmann
                                (GANETI_RAPI_VERSION, instance)), None, body)
995 c744425f Michael Hanselmann
996 c744425f Michael Hanselmann
    # Use old request format
997 c744425f Michael Hanselmann
    if osparams:
998 c744425f Michael Hanselmann
      raise GanetiApiError("Server does not support specifying OS parameters"
999 c744425f Michael Hanselmann
                           " for instance reinstallation")
1000 c744425f Michael Hanselmann
1001 fcee9675 David Knowles
    query = []
1002 4c864b55 Michael Hanselmann
    _AppendIf(query, os, ("os", os))
1003 4c864b55 Michael Hanselmann
    _AppendIf(query, no_startup, ("nostartup", 1))
1004 4c864b55 Michael Hanselmann
1005 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1006 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reinstall" %
1007 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1008 95ab4de9 David Knowles
1009 bfc2002f Michael Hanselmann
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1010 539d65ba Michael Hanselmann
                           remote_node=None, iallocator=None):
1011 95ab4de9 David Knowles
    """Replaces disks on an instance.
1012 95ab4de9 David Knowles

1013 95ab4de9 David Knowles
    @type instance: str
1014 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1015 bfc2002f Michael Hanselmann
    @type disks: list of ints
1016 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1017 95ab4de9 David Knowles
    @type mode: str
1018 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1019 95ab4de9 David Knowles
    @type remote_node: str or None
1020 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1021 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1022 95ab4de9 David Knowles
    @type iallocator: str or None
1023 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1024 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1025 95ab4de9 David Knowles

1026 98805538 Michael Hanselmann
    @rtype: string
1027 95ab4de9 David Knowles
    @return: job id
1028 95ab4de9 David Knowles

1029 95ab4de9 David Knowles
    """
1030 cfc03c54 Michael Hanselmann
    query = [
1031 cfc03c54 Michael Hanselmann
      ("mode", mode),
1032 cfc03c54 Michael Hanselmann
      ]
1033 95ab4de9 David Knowles
1034 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1035 539d65ba Michael Hanselmann
1036 539d65ba Michael Hanselmann
    if disks is not None:
1037 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1038 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1039 bfc2002f Michael Hanselmann
1040 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1041 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1042 bfc2002f Michael Hanselmann
1043 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1044 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1045 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1046 95ab4de9 David Knowles
1047 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1048 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1049 ebeb600f Michael Hanselmann

1050 ebeb600f Michael Hanselmann
    @type instance: string
1051 ebeb600f Michael Hanselmann
    @param instance: Instance name
1052 ebeb600f Michael Hanselmann
    @type mode: string
1053 ebeb600f Michael Hanselmann
    @param mode: Export mode
1054 ebeb600f Michael Hanselmann
    @rtype: string
1055 ebeb600f Michael Hanselmann
    @return: Job ID
1056 ebeb600f Michael Hanselmann

1057 ebeb600f Michael Hanselmann
    """
1058 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1059 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1060 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1061 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1062 ebeb600f Michael Hanselmann
1063 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1064 ebeb600f Michael Hanselmann
                     remove_instance=None,
1065 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1066 ebeb600f Michael Hanselmann
    """Exports an instance.
1067 ebeb600f Michael Hanselmann

1068 ebeb600f Michael Hanselmann
    @type instance: string
1069 ebeb600f Michael Hanselmann
    @param instance: Instance name
1070 ebeb600f Michael Hanselmann
    @type mode: string
1071 ebeb600f Michael Hanselmann
    @param mode: Export mode
1072 ebeb600f Michael Hanselmann
    @rtype: string
1073 ebeb600f Michael Hanselmann
    @return: Job ID
1074 ebeb600f Michael Hanselmann

1075 ebeb600f Michael Hanselmann
    """
1076 ebeb600f Michael Hanselmann
    body = {
1077 ebeb600f Michael Hanselmann
      "destination": destination,
1078 ebeb600f Michael Hanselmann
      "mode": mode,
1079 ebeb600f Michael Hanselmann
      }
1080 ebeb600f Michael Hanselmann
1081 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1082 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1083 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1084 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1085 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1086 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1087 ebeb600f Michael Hanselmann
1088 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1089 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1090 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1091 ebeb600f Michael Hanselmann
1092 e0ac6ce6 Michael Hanselmann
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1093 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1094 e0ac6ce6 Michael Hanselmann

1095 e0ac6ce6 Michael Hanselmann
    @type instance: string
1096 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1097 e0ac6ce6 Michael Hanselmann
    @type mode: string
1098 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1099 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1100 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1101 d914c76f Simeon Miteff
    @rtype: string
1102 d914c76f Simeon Miteff
    @return: job id
1103 e0ac6ce6 Michael Hanselmann

1104 e0ac6ce6 Michael Hanselmann
    """
1105 e0ac6ce6 Michael Hanselmann
    body = {}
1106 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1107 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1108 e0ac6ce6 Michael Hanselmann
1109 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1110 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1111 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1112 e0ac6ce6 Michael Hanselmann
1113 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1114 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1115 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1116 c0a146a1 Michael Hanselmann

1117 c0a146a1 Michael Hanselmann
    @type instance: string
1118 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1119 c0a146a1 Michael Hanselmann
    @type iallocator: string
1120 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1121 c0a146a1 Michael Hanselmann
      shared-storage instances
1122 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1123 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1124 c0a146a1 Michael Hanselmann
    @type target_node: string
1125 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1126 c0a146a1 Michael Hanselmann
    @rtype: string
1127 c0a146a1 Michael Hanselmann
    @return: job id
1128 c0a146a1 Michael Hanselmann

1129 c0a146a1 Michael Hanselmann
    """
1130 c0a146a1 Michael Hanselmann
    body = {}
1131 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1132 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1133 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1134 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1135 c0a146a1 Michael Hanselmann
1136 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1137 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1138 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1139 c0a146a1 Michael Hanselmann
1140 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1141 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1142 d654aae1 Michael Hanselmann

1143 d654aae1 Michael Hanselmann
    @type instance: string
1144 d654aae1 Michael Hanselmann
    @param instance: Instance name
1145 d654aae1 Michael Hanselmann
    @type new_name: string
1146 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1147 d654aae1 Michael Hanselmann
    @type ip_check: bool
1148 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1149 d654aae1 Michael Hanselmann
    @type name_check: bool
1150 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1151 d914c76f Simeon Miteff
    @rtype: string
1152 d914c76f Simeon Miteff
    @return: job id
1153 d654aae1 Michael Hanselmann

1154 d654aae1 Michael Hanselmann
    """
1155 d654aae1 Michael Hanselmann
    body = {
1156 d654aae1 Michael Hanselmann
      "new_name": new_name,
1157 d654aae1 Michael Hanselmann
      }
1158 d654aae1 Michael Hanselmann
1159 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1160 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1161 d654aae1 Michael Hanselmann
1162 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1163 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1164 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1165 d654aae1 Michael Hanselmann
1166 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1167 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1168 b82d4c5e Michael Hanselmann

1169 b82d4c5e Michael Hanselmann
    @type instance: string
1170 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1171 d914c76f Simeon Miteff
    @rtype: dict
1172 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1173 b82d4c5e Michael Hanselmann

1174 b82d4c5e Michael Hanselmann
    """
1175 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1176 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1177 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1178 b82d4c5e Michael Hanselmann
1179 95ab4de9 David Knowles
  def GetJobs(self):
1180 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1181 95ab4de9 David Knowles

1182 95ab4de9 David Knowles
    @rtype: list of int
1183 95ab4de9 David Knowles
    @return: job ids for the cluster
1184 95ab4de9 David Knowles

1185 95ab4de9 David Knowles
    """
1186 768747ed Michael Hanselmann
    return [int(j["id"])
1187 a198b2d9 Michael Hanselmann
            for j in self._SendRequest(HTTP_GET,
1188 a198b2d9 Michael Hanselmann
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1189 a198b2d9 Michael Hanselmann
                                       None, None)]
1190 95ab4de9 David Knowles
1191 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1192 95ab4de9 David Knowles
    """Gets the status of a job.
1193 95ab4de9 David Knowles

1194 98805538 Michael Hanselmann
    @type job_id: string
1195 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1196 95ab4de9 David Knowles

1197 95ab4de9 David Knowles
    @rtype: dict
1198 95ab4de9 David Knowles
    @return: job status
1199 95ab4de9 David Knowles

1200 95ab4de9 David Knowles
    """
1201 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1202 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1203 a198b2d9 Michael Hanselmann
                             None, None)
1204 95ab4de9 David Knowles
1205 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1206 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1207 16c13387 Theo Van Dinter

1208 dde0e97c Michael Hanselmann
    Completion is defined as any of the following states listed in
1209 dde0e97c Michael Hanselmann
    L{JOB_STATUS_FINALIZED}.
1210 16c13387 Theo Van Dinter

1211 cfda0e48 Iustin Pop
    @type job_id: string
1212 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1213 16c13387 Theo Van Dinter
    @type period: int
1214 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1215 16c13387 Theo Van Dinter
    @type retries: int
1216 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1217 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1218 16c13387 Theo Van Dinter

1219 16c13387 Theo Van Dinter
    @rtype: bool
1220 dde0e97c Michael Hanselmann
    @return: C{True} if job succeeded or C{False} if failed/status timeout
1221 dde0e97c Michael Hanselmann
    @deprecated: It is recommended to use L{WaitForJobChange} wherever
1222 dde0e97c Michael Hanselmann
      possible; L{WaitForJobChange} returns immediately after a job changed and
1223 dde0e97c Michael Hanselmann
      does not use polling
1224 cfda0e48 Iustin Pop

1225 16c13387 Theo Van Dinter
    """
1226 16c13387 Theo Van Dinter
    while retries != 0:
1227 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1228 dde0e97c Michael Hanselmann
1229 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1230 16c13387 Theo Van Dinter
        return True
1231 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1232 dde0e97c Michael Hanselmann
        return False
1233 dde0e97c Michael Hanselmann
1234 dde0e97c Michael Hanselmann
      if period:
1235 dde0e97c Michael Hanselmann
        time.sleep(period)
1236 dde0e97c Michael Hanselmann
1237 16c13387 Theo Van Dinter
      if retries > 0:
1238 16c13387 Theo Van Dinter
        retries -= 1
1239 dde0e97c Michael Hanselmann
1240 16c13387 Theo Van Dinter
    return False
1241 16c13387 Theo Van Dinter
1242 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1243 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1244 d9b67f70 Michael Hanselmann

1245 98805538 Michael Hanselmann
    @type job_id: string
1246 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1247 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1248 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1249 d914c76f Simeon Miteff
    @rtype: dict
1250 d9b67f70 Michael Hanselmann

1251 d9b67f70 Michael Hanselmann
    """
1252 d9b67f70 Michael Hanselmann
    body = {
1253 d9b67f70 Michael Hanselmann
      "fields": fields,
1254 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1255 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1256 d9b67f70 Michael Hanselmann
      }
1257 d9b67f70 Michael Hanselmann
1258 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1259 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1260 a198b2d9 Michael Hanselmann
                             None, body)
1261 d9b67f70 Michael Hanselmann
1262 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1263 cf9ada49 Michael Hanselmann
    """Cancels a job.
1264 95ab4de9 David Knowles

1265 98805538 Michael Hanselmann
    @type job_id: string
1266 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1267 95ab4de9 David Knowles
    @type dry_run: bool
1268 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1269 d914c76f Simeon Miteff
    @rtype: tuple
1270 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1271 95ab4de9 David Knowles

1272 95ab4de9 David Knowles
    """
1273 95ab4de9 David Knowles
    query = []
1274 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1275 95ab4de9 David Knowles
1276 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1277 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1278 a198b2d9 Michael Hanselmann
                             query, None)
1279 95ab4de9 David Knowles
1280 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1281 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1282 95ab4de9 David Knowles

1283 95ab4de9 David Knowles
    @type bulk: bool
1284 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1285 95ab4de9 David Knowles

1286 95ab4de9 David Knowles
    @rtype: list of dict or str
1287 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1288 95ab4de9 David Knowles
        else list of nodes in the cluster
1289 95ab4de9 David Knowles

1290 95ab4de9 David Knowles
    """
1291 95ab4de9 David Knowles
    query = []
1292 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1293 95ab4de9 David Knowles
1294 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1295 a198b2d9 Michael Hanselmann
                              query, None)
1296 95ab4de9 David Knowles
    if bulk:
1297 95ab4de9 David Knowles
      return nodes
1298 95ab4de9 David Knowles
    else:
1299 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1300 95ab4de9 David Knowles
1301 591e5103 Michael Hanselmann
  def GetNode(self, node):
1302 95ab4de9 David Knowles
    """Gets information about a node.
1303 95ab4de9 David Knowles

1304 95ab4de9 David Knowles
    @type node: str
1305 95ab4de9 David Knowles
    @param node: node whose info to return
1306 95ab4de9 David Knowles

1307 95ab4de9 David Knowles
    @rtype: dict
1308 95ab4de9 David Knowles
    @return: info about the node
1309 95ab4de9 David Knowles

1310 95ab4de9 David Knowles
    """
1311 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1312 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1313 a198b2d9 Michael Hanselmann
                             None, None)
1314 95ab4de9 David Knowles
1315 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1316 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1317 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1318 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1319 95ab4de9 David Knowles

1320 95ab4de9 David Knowles
    @type node: str
1321 95ab4de9 David Knowles
    @param node: node to evacuate
1322 95ab4de9 David Knowles
    @type iallocator: str or None
1323 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1324 95ab4de9 David Knowles
    @type remote_node: str
1325 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1326 95ab4de9 David Knowles
    @type dry_run: bool
1327 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1328 941b9309 Iustin Pop
    @type early_release: bool
1329 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1330 0b58db81 Michael Hanselmann
    @type mode: string
1331 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1332 de40437a Michael Hanselmann
    @type accept_old: bool
1333 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1334 de40437a Michael Hanselmann
        results
1335 de40437a Michael Hanselmann

1336 de40437a Michael Hanselmann
    @rtype: string, or a list for pre-2.5 results
1337 de40437a Michael Hanselmann
    @return: Job ID or, if C{accept_old} is set and server is pre-2.5,
1338 de40437a Michael Hanselmann
      list of (job ID, instance name, new secondary node); if dry_run was
1339 de40437a Michael Hanselmann
      specified, then the actual move jobs were not submitted and the job IDs
1340 de40437a Michael Hanselmann
      will be C{None}
1341 95ab4de9 David Knowles

1342 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1343 941b9309 Iustin Pop
        specified
1344 95ab4de9 David Knowles

1345 95ab4de9 David Knowles
    """
1346 95ab4de9 David Knowles
    if iallocator and remote_node:
1347 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1348 95ab4de9 David Knowles
1349 cfc03c54 Michael Hanselmann
    query = []
1350 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1351 de40437a Michael Hanselmann
1352 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1353 0b58db81 Michael Hanselmann
      # Server supports body parameters
1354 de40437a Michael Hanselmann
      body = {}
1355 de40437a Michael Hanselmann
1356 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1357 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1358 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1359 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1360 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1361 de40437a Michael Hanselmann
    else:
1362 de40437a Michael Hanselmann
      # Pre-2.5 request format
1363 de40437a Michael Hanselmann
      body = None
1364 de40437a Michael Hanselmann
1365 de40437a Michael Hanselmann
      if not accept_old:
1366 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1367 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1368 de40437a Michael Hanselmann
                             " accept_old)")
1369 de40437a Michael Hanselmann
1370 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1371 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1372 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1373 de40437a Michael Hanselmann
1374 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1375 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1376 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1377 95ab4de9 David Knowles
1378 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1379 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1380 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1381 95ab4de9 David Knowles
1382 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1383 b7a1c816 Michael Hanselmann
                  target_node=None):
1384 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1385 95ab4de9 David Knowles

1386 95ab4de9 David Knowles
    @type node: str
1387 95ab4de9 David Knowles
    @param node: node to migrate
1388 1f334d96 Iustin Pop
    @type mode: string
1389 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1390 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1391 95ab4de9 David Knowles
    @type dry_run: bool
1392 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1393 b7a1c816 Michael Hanselmann
    @type iallocator: string
1394 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1395 b7a1c816 Michael Hanselmann
    @type target_node: string
1396 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1397 95ab4de9 David Knowles

1398 98805538 Michael Hanselmann
    @rtype: string
1399 95ab4de9 David Knowles
    @return: job id
1400 95ab4de9 David Knowles

1401 95ab4de9 David Knowles
    """
1402 95ab4de9 David Knowles
    query = []
1403 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1404 95ab4de9 David Knowles
1405 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1406 b7a1c816 Michael Hanselmann
      body = {}
1407 b7a1c816 Michael Hanselmann
1408 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1409 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1410 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1411 b7a1c816 Michael Hanselmann
1412 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1413 b7a1c816 Michael Hanselmann
1414 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1415 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1416 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1417 b7a1c816 Michael Hanselmann
    else:
1418 b7a1c816 Michael Hanselmann
      # Use old request format
1419 b7a1c816 Michael Hanselmann
      if target_node is not None:
1420 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1421 b7a1c816 Michael Hanselmann
                             " for node migration")
1422 b7a1c816 Michael Hanselmann
1423 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1424 b7a1c816 Michael Hanselmann
1425 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1426 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1427 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1428 95ab4de9 David Knowles
1429 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1430 95ab4de9 David Knowles
    """Gets the current role for a node.
1431 95ab4de9 David Knowles

1432 95ab4de9 David Knowles
    @type node: str
1433 95ab4de9 David Knowles
    @param node: node whose role to return
1434 95ab4de9 David Knowles

1435 95ab4de9 David Knowles
    @rtype: str
1436 95ab4de9 David Knowles
    @return: the current role for a node
1437 95ab4de9 David Knowles

1438 95ab4de9 David Knowles
    """
1439 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1440 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1441 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1442 95ab4de9 David Knowles
1443 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1444 95ab4de9 David Knowles
    """Sets the role for a node.
1445 95ab4de9 David Knowles

1446 95ab4de9 David Knowles
    @type node: str
1447 95ab4de9 David Knowles
    @param node: the node whose role to set
1448 95ab4de9 David Knowles
    @type role: str
1449 95ab4de9 David Knowles
    @param role: the role to set for the node
1450 95ab4de9 David Knowles
    @type force: bool
1451 95ab4de9 David Knowles
    @param force: whether to force the role change
1452 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1453 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1454 8de8e68d Michael Hanselmann
                         if necessary
1455 95ab4de9 David Knowles

1456 98805538 Michael Hanselmann
    @rtype: string
1457 95ab4de9 David Knowles
    @return: job id
1458 95ab4de9 David Knowles

1459 95ab4de9 David Knowles
    """
1460 4c864b55 Michael Hanselmann
    query = []
1461 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1462 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1463 8de8e68d Michael Hanselmann
1464 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1465 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1466 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1467 95ab4de9 David Knowles
1468 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1469 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1470 42d4d8b9 Michael Hanselmann

1471 42d4d8b9 Michael Hanselmann
    @type node: string
1472 42d4d8b9 Michael Hanselmann
    @param node: Node name
1473 42d4d8b9 Michael Hanselmann
    @type force: bool
1474 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1475 42d4d8b9 Michael Hanselmann
    @rtype: string
1476 42d4d8b9 Michael Hanselmann
    @return: job id
1477 42d4d8b9 Michael Hanselmann

1478 42d4d8b9 Michael Hanselmann
    """
1479 4c864b55 Michael Hanselmann
    query = []
1480 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1481 42d4d8b9 Michael Hanselmann
1482 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1483 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1484 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1485 42d4d8b9 Michael Hanselmann
1486 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1487 94497dd1 Michael Hanselmann
    """Modifies a node.
1488 94497dd1 Michael Hanselmann

1489 94497dd1 Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
1490 94497dd1 Michael Hanselmann

1491 370f2042 Guido Trotter
    @type node: string
1492 370f2042 Guido Trotter
    @param node: Node name
1493 94497dd1 Michael Hanselmann
    @rtype: string
1494 94497dd1 Michael Hanselmann
    @return: job id
1495 94497dd1 Michael Hanselmann

1496 94497dd1 Michael Hanselmann
    """
1497 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1498 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1499 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1500 94497dd1 Michael Hanselmann
1501 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1502 95ab4de9 David Knowles
    """Gets the storage units for a node.
1503 95ab4de9 David Knowles

1504 95ab4de9 David Knowles
    @type node: str
1505 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1506 95ab4de9 David Knowles
    @type storage_type: str
1507 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1508 95ab4de9 David Knowles
    @type output_fields: str
1509 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1510 95ab4de9 David Knowles

1511 98805538 Michael Hanselmann
    @rtype: string
1512 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1513 95ab4de9 David Knowles

1514 95ab4de9 David Knowles
    """
1515 cfc03c54 Michael Hanselmann
    query = [
1516 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1517 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1518 cfc03c54 Michael Hanselmann
      ]
1519 95ab4de9 David Knowles
1520 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1521 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1522 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1523 95ab4de9 David Knowles
1524 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1525 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1526 95ab4de9 David Knowles

1527 95ab4de9 David Knowles
    @type node: str
1528 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1529 95ab4de9 David Knowles
    @type storage_type: str
1530 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1531 95ab4de9 David Knowles
    @type name: str
1532 95ab4de9 David Knowles
    @param name: name of the storage unit
1533 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1534 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1535 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1536 95ab4de9 David Knowles

1537 98805538 Michael Hanselmann
    @rtype: string
1538 95ab4de9 David Knowles
    @return: job id
1539 95ab4de9 David Knowles

1540 95ab4de9 David Knowles
    """
1541 95ab4de9 David Knowles
    query = [
1542 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1543 cfc03c54 Michael Hanselmann
      ("name", name),
1544 cfc03c54 Michael Hanselmann
      ]
1545 cfc03c54 Michael Hanselmann
1546 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1547 fde28316 Michael Hanselmann
1548 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1549 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1550 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1551 95ab4de9 David Knowles
1552 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1553 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1554 95ab4de9 David Knowles

1555 95ab4de9 David Knowles
    @type node: str
1556 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1557 95ab4de9 David Knowles
    @type storage_type: str
1558 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1559 95ab4de9 David Knowles
    @type name: str
1560 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1561 95ab4de9 David Knowles

1562 98805538 Michael Hanselmann
    @rtype: string
1563 95ab4de9 David Knowles
    @return: job id
1564 95ab4de9 David Knowles

1565 95ab4de9 David Knowles
    """
1566 cfc03c54 Michael Hanselmann
    query = [
1567 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1568 cfc03c54 Michael Hanselmann
      ("name", name),
1569 cfc03c54 Michael Hanselmann
      ]
1570 95ab4de9 David Knowles
1571 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1572 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1573 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1574 95ab4de9 David Knowles
1575 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1576 95ab4de9 David Knowles
    """Gets the tags for a node.
1577 95ab4de9 David Knowles

1578 95ab4de9 David Knowles
    @type node: str
1579 95ab4de9 David Knowles
    @param node: node whose tags to return
1580 95ab4de9 David Knowles

1581 95ab4de9 David Knowles
    @rtype: list of str
1582 95ab4de9 David Knowles
    @return: tags for the node
1583 95ab4de9 David Knowles

1584 95ab4de9 David Knowles
    """
1585 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1586 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1587 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1588 95ab4de9 David Knowles
1589 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1590 95ab4de9 David Knowles
    """Adds tags to a node.
1591 95ab4de9 David Knowles

1592 95ab4de9 David Knowles
    @type node: str
1593 95ab4de9 David Knowles
    @param node: node to add tags to
1594 95ab4de9 David Knowles
    @type tags: list of str
1595 95ab4de9 David Knowles
    @param tags: tags to add to the node
1596 95ab4de9 David Knowles
    @type dry_run: bool
1597 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1598 95ab4de9 David Knowles

1599 98805538 Michael Hanselmann
    @rtype: string
1600 95ab4de9 David Knowles
    @return: job id
1601 95ab4de9 David Knowles

1602 95ab4de9 David Knowles
    """
1603 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1604 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1605 95ab4de9 David Knowles
1606 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1607 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1608 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1609 95ab4de9 David Knowles
1610 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1611 95ab4de9 David Knowles
    """Delete tags from a node.
1612 95ab4de9 David Knowles

1613 95ab4de9 David Knowles
    @type node: str
1614 95ab4de9 David Knowles
    @param node: node to remove tags from
1615 95ab4de9 David Knowles
    @type tags: list of str
1616 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1617 95ab4de9 David Knowles
    @type dry_run: bool
1618 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1619 95ab4de9 David Knowles

1620 98805538 Michael Hanselmann
    @rtype: string
1621 95ab4de9 David Knowles
    @return: job id
1622 95ab4de9 David Knowles

1623 95ab4de9 David Knowles
    """
1624 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1625 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1626 95ab4de9 David Knowles
1627 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1628 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1629 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1630 a268af8d Adeodato Simo
1631 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1632 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1633 a268af8d Adeodato Simo

1634 a268af8d Adeodato Simo
    @type bulk: bool
1635 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1636 a268af8d Adeodato Simo

1637 a268af8d Adeodato Simo
    @rtype: list of dict or str
1638 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1639 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1640 a268af8d Adeodato Simo

1641 a268af8d Adeodato Simo
    """
1642 a268af8d Adeodato Simo
    query = []
1643 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1644 a268af8d Adeodato Simo
1645 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1646 a268af8d Adeodato Simo
                               query, None)
1647 a268af8d Adeodato Simo
    if bulk:
1648 a268af8d Adeodato Simo
      return groups
1649 a268af8d Adeodato Simo
    else:
1650 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1651 a268af8d Adeodato Simo
1652 a268af8d Adeodato Simo
  def GetGroup(self, group):
1653 a268af8d Adeodato Simo
    """Gets information about a node group.
1654 a268af8d Adeodato Simo

1655 a268af8d Adeodato Simo
    @type group: str
1656 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1657 a268af8d Adeodato Simo

1658 a268af8d Adeodato Simo
    @rtype: dict
1659 a268af8d Adeodato Simo
    @return: info about the node group
1660 a268af8d Adeodato Simo

1661 a268af8d Adeodato Simo
    """
1662 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1663 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1664 a268af8d Adeodato Simo
                             None, None)
1665 a268af8d Adeodato Simo
1666 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1667 a268af8d Adeodato Simo
    """Creates a new node group.
1668 a268af8d Adeodato Simo

1669 a268af8d Adeodato Simo
    @type name: str
1670 a268af8d Adeodato Simo
    @param name: the name of node group to create
1671 90e99856 Adeodato Simo
    @type alloc_policy: str
1672 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
1673 a268af8d Adeodato Simo
    @type dry_run: bool
1674 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1675 a268af8d Adeodato Simo

1676 98805538 Michael Hanselmann
    @rtype: string
1677 a268af8d Adeodato Simo
    @return: job id
1678 a268af8d Adeodato Simo

1679 a268af8d Adeodato Simo
    """
1680 a268af8d Adeodato Simo
    query = []
1681 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1682 a268af8d Adeodato Simo
1683 a268af8d Adeodato Simo
    body = {
1684 a268af8d Adeodato Simo
      "name": name,
1685 90e99856 Adeodato Simo
      "alloc_policy": alloc_policy
1686 a268af8d Adeodato Simo
      }
1687 a268af8d Adeodato Simo
1688 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1689 a268af8d Adeodato Simo
                             query, body)
1690 a268af8d Adeodato Simo
1691 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
1692 f18fab7d Adeodato Simo
    """Modifies a node group.
1693 f18fab7d Adeodato Simo

1694 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
1695 f18fab7d Adeodato Simo

1696 f18fab7d Adeodato Simo
    @type group: string
1697 f18fab7d Adeodato Simo
    @param group: Node group name
1698 98805538 Michael Hanselmann
    @rtype: string
1699 f18fab7d Adeodato Simo
    @return: job id
1700 f18fab7d Adeodato Simo

1701 f18fab7d Adeodato Simo
    """
1702 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1703 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
1704 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1705 f18fab7d Adeodato Simo
1706 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
1707 a268af8d Adeodato Simo
    """Deletes a node group.
1708 a268af8d Adeodato Simo

1709 a268af8d Adeodato Simo
    @type group: str
1710 a268af8d Adeodato Simo
    @param group: the node group to delete
1711 a268af8d Adeodato Simo
    @type dry_run: bool
1712 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1713 a268af8d Adeodato Simo

1714 98805538 Michael Hanselmann
    @rtype: string
1715 a268af8d Adeodato Simo
    @return: job id
1716 a268af8d Adeodato Simo

1717 a268af8d Adeodato Simo
    """
1718 a268af8d Adeodato Simo
    query = []
1719 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1720 a268af8d Adeodato Simo
1721 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
1722 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
1723 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
1724 a268af8d Adeodato Simo
1725 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
1726 a268af8d Adeodato Simo
    """Changes the name of a node group.
1727 a268af8d Adeodato Simo

1728 a268af8d Adeodato Simo
    @type group: string
1729 a268af8d Adeodato Simo
    @param group: Node group name
1730 a268af8d Adeodato Simo
    @type new_name: string
1731 a268af8d Adeodato Simo
    @param new_name: New node group name
1732 a268af8d Adeodato Simo

1733 98805538 Michael Hanselmann
    @rtype: string
1734 a268af8d Adeodato Simo
    @return: job id
1735 a268af8d Adeodato Simo

1736 a268af8d Adeodato Simo
    """
1737 a268af8d Adeodato Simo
    body = {
1738 a268af8d Adeodato Simo
      "new_name": new_name,
1739 a268af8d Adeodato Simo
      }
1740 a268af8d Adeodato Simo
1741 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1742 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
1743 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
1744 4245446f Adeodato Simo
1745 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
1746 4245446f Adeodato Simo
    """Assigns nodes to a group.
1747 4245446f Adeodato Simo

1748 4245446f Adeodato Simo
    @type group: string
1749 4245446f Adeodato Simo
    @param group: Node gropu name
1750 4245446f Adeodato Simo
    @type nodes: list of strings
1751 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
1752 4245446f Adeodato Simo

1753 98805538 Michael Hanselmann
    @rtype: string
1754 4245446f Adeodato Simo
    @return: job id
1755 4245446f Adeodato Simo

1756 4245446f Adeodato Simo
    """
1757 4245446f Adeodato Simo
    query = []
1758 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1759 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1760 4245446f Adeodato Simo
1761 4245446f Adeodato Simo
    body = {
1762 4245446f Adeodato Simo
      "nodes": nodes,
1763 4245446f Adeodato Simo
      }
1764 4245446f Adeodato Simo
1765 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1766 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
1767 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
1768 208a6cff Michael Hanselmann
1769 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
1770 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
1771 414ebaf1 Michael Hanselmann

1772 414ebaf1 Michael Hanselmann
    @type group: string
1773 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
1774 414ebaf1 Michael Hanselmann

1775 414ebaf1 Michael Hanselmann
    @rtype: list of strings
1776 414ebaf1 Michael Hanselmann
    @return: tags for the group
1777 414ebaf1 Michael Hanselmann

1778 414ebaf1 Michael Hanselmann
    """
1779 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1780 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1781 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
1782 414ebaf1 Michael Hanselmann
1783 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
1784 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
1785 414ebaf1 Michael Hanselmann

1786 414ebaf1 Michael Hanselmann
    @type group: str
1787 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
1788 414ebaf1 Michael Hanselmann
    @type tags: list of string
1789 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
1790 414ebaf1 Michael Hanselmann
    @type dry_run: bool
1791 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
1792 414ebaf1 Michael Hanselmann

1793 414ebaf1 Michael Hanselmann
    @rtype: string
1794 414ebaf1 Michael Hanselmann
    @return: job id
1795 414ebaf1 Michael Hanselmann

1796 414ebaf1 Michael Hanselmann
    """
1797 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
1798 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1799 414ebaf1 Michael Hanselmann
1800 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1801 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1802 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
1803 414ebaf1 Michael Hanselmann
1804 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
1805 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
1806 414ebaf1 Michael Hanselmann

1807 414ebaf1 Michael Hanselmann
    @type group: str
1808 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
1809 414ebaf1 Michael Hanselmann
    @type tags: list of string
1810 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
1811 414ebaf1 Michael Hanselmann
    @type dry_run: bool
1812 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
1813 414ebaf1 Michael Hanselmann
    @rtype: string
1814 414ebaf1 Michael Hanselmann
    @return: job id
1815 414ebaf1 Michael Hanselmann

1816 414ebaf1 Michael Hanselmann
    """
1817 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
1818 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1819 414ebaf1 Michael Hanselmann
1820 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1821 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1822 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
1823 414ebaf1 Michael Hanselmann
1824 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
1825 208a6cff Michael Hanselmann
    """Retrieves information about resources.
1826 208a6cff Michael Hanselmann

1827 208a6cff Michael Hanselmann
    @type what: string
1828 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1829 208a6cff Michael Hanselmann
    @type fields: list of string
1830 208a6cff Michael Hanselmann
    @param fields: Requested fields
1831 2e5c33db Iustin Pop
    @type qfilter: None or list
1832 2e5c33db Iustin Pop
    @param qfilter: Query filter
1833 208a6cff Michael Hanselmann

1834 208a6cff Michael Hanselmann
    @rtype: string
1835 208a6cff Michael Hanselmann
    @return: job id
1836 208a6cff Michael Hanselmann

1837 208a6cff Michael Hanselmann
    """
1838 208a6cff Michael Hanselmann
    body = {
1839 208a6cff Michael Hanselmann
      "fields": fields,
1840 208a6cff Michael Hanselmann
      }
1841 208a6cff Michael Hanselmann
1842 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
1843 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
1844 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
1845 208a6cff Michael Hanselmann
1846 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1847 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
1848 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
1849 208a6cff Michael Hanselmann
1850 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
1851 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
1852 208a6cff Michael Hanselmann

1853 208a6cff Michael Hanselmann
    @type what: string
1854 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1855 208a6cff Michael Hanselmann
    @type fields: list of string
1856 208a6cff Michael Hanselmann
    @param fields: Requested fields
1857 208a6cff Michael Hanselmann

1858 208a6cff Michael Hanselmann
    @rtype: string
1859 208a6cff Michael Hanselmann
    @return: job id
1860 208a6cff Michael Hanselmann

1861 208a6cff Michael Hanselmann
    """
1862 208a6cff Michael Hanselmann
    query = []
1863 208a6cff Michael Hanselmann
1864 208a6cff Michael Hanselmann
    if fields is not None:
1865 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
1866 208a6cff Michael Hanselmann
1867 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1868 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
1869 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)