Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 31d3b918

History | View | Annotate | Download (63.7 kB)

1 95ab4de9 David Knowles
#
2 95ab4de9 David Knowles
#
3 95ab4de9 David Knowles
4 6ce90543 Michael Hanselmann
# Copyright (C) 2010, 2011, 2012 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 1eaddbc6 Michael Hanselmann
JOB_STATUS_PENDING = frozenset([
84 1eaddbc6 Michael Hanselmann
  JOB_STATUS_QUEUED,
85 1eaddbc6 Michael Hanselmann
  JOB_STATUS_WAITING,
86 1eaddbc6 Michael Hanselmann
  JOB_STATUS_CANCELING,
87 1eaddbc6 Michael Hanselmann
  ])
88 63d5eb8a Michael Hanselmann
JOB_STATUS_FINALIZED = frozenset([
89 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELED,
90 63d5eb8a Michael Hanselmann
  JOB_STATUS_SUCCESS,
91 63d5eb8a Michael Hanselmann
  JOB_STATUS_ERROR,
92 63d5eb8a Michael Hanselmann
  ])
93 63d5eb8a Michael Hanselmann
JOB_STATUS_ALL = frozenset([
94 63d5eb8a Michael Hanselmann
  JOB_STATUS_RUNNING,
95 1eaddbc6 Michael Hanselmann
  ]) | JOB_STATUS_PENDING | JOB_STATUS_FINALIZED
96 63d5eb8a Michael Hanselmann
97 47099cd1 Michael Hanselmann
# Legacy name
98 47099cd1 Michael Hanselmann
JOB_STATUS_WAITLOCK = JOB_STATUS_WAITING
99 47099cd1 Michael Hanselmann
100 8a47b447 Michael Hanselmann
# Internal constants
101 8a47b447 Michael Hanselmann
_REQ_DATA_VERSION_FIELD = "__version__"
102 4c864b55 Michael Hanselmann
_QPARAM_DRY_RUN = "dry-run"
103 4c864b55 Michael Hanselmann
_QPARAM_FORCE = "force"
104 8a47b447 Michael Hanselmann
105 6396dc04 Michael Hanselmann
# Feature strings
106 6396dc04 Michael Hanselmann
INST_CREATE_REQV1 = "instance-create-reqv1"
107 6396dc04 Michael Hanselmann
INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
108 6396dc04 Michael Hanselmann
NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
109 6396dc04 Michael Hanselmann
NODE_EVAC_RES1 = "node-evac-res1"
110 6396dc04 Michael Hanselmann
111 6396dc04 Michael Hanselmann
# Old feature constant names in case they're references by users of this module
112 6396dc04 Michael Hanselmann
_INST_CREATE_REQV1 = INST_CREATE_REQV1
113 6396dc04 Michael Hanselmann
_INST_REINSTALL_REQV1 = INST_REINSTALL_REQV1
114 6396dc04 Michael Hanselmann
_NODE_MIGRATE_REQV1 = NODE_MIGRATE_REQV1
115 6396dc04 Michael Hanselmann
_NODE_EVAC_RES1 = NODE_EVAC_RES1
116 6396dc04 Michael Hanselmann
117 bc826292 Michael Hanselmann
#: Resolver errors
118 bc826292 Michael Hanselmann
ECODE_RESOLVER = "resolver_error"
119 bc826292 Michael Hanselmann
120 bc826292 Michael Hanselmann
#: Not enough resources (iallocator failure, disk space, memory, etc.)
121 bc826292 Michael Hanselmann
ECODE_NORES = "insufficient_resources"
122 bc826292 Michael Hanselmann
123 bc826292 Michael Hanselmann
#: Temporarily out of resources; operation can be tried again
124 e2dd6ece Michael Hanselmann
ECODE_TEMP_NORES = "temp_insufficient_resources"
125 bc826292 Michael Hanselmann
126 bc826292 Michael Hanselmann
#: Wrong arguments (at syntax level)
127 bc826292 Michael Hanselmann
ECODE_INVAL = "wrong_input"
128 bc826292 Michael Hanselmann
129 bc826292 Michael Hanselmann
#: Wrong entity state
130 bc826292 Michael Hanselmann
ECODE_STATE = "wrong_state"
131 bc826292 Michael Hanselmann
132 bc826292 Michael Hanselmann
#: Entity not found
133 bc826292 Michael Hanselmann
ECODE_NOENT = "unknown_entity"
134 bc826292 Michael Hanselmann
135 bc826292 Michael Hanselmann
#: Entity already exists
136 bc826292 Michael Hanselmann
ECODE_EXISTS = "already_exists"
137 bc826292 Michael Hanselmann
138 bc826292 Michael Hanselmann
#: Resource not unique (e.g. MAC or IP duplication)
139 bc826292 Michael Hanselmann
ECODE_NOTUNIQUE = "resource_not_unique"
140 bc826292 Michael Hanselmann
141 bc826292 Michael Hanselmann
#: Internal cluster error
142 bc826292 Michael Hanselmann
ECODE_FAULT = "internal_error"
143 bc826292 Michael Hanselmann
144 bc826292 Michael Hanselmann
#: Environment error (e.g. node disk error)
145 bc826292 Michael Hanselmann
ECODE_ENVIRON = "environment_error"
146 bc826292 Michael Hanselmann
147 bc826292 Michael Hanselmann
#: List of all failure types
148 bc826292 Michael Hanselmann
ECODE_ALL = frozenset([
149 bc826292 Michael Hanselmann
  ECODE_RESOLVER,
150 bc826292 Michael Hanselmann
  ECODE_NORES,
151 bc826292 Michael Hanselmann
  ECODE_TEMP_NORES,
152 bc826292 Michael Hanselmann
  ECODE_INVAL,
153 bc826292 Michael Hanselmann
  ECODE_STATE,
154 bc826292 Michael Hanselmann
  ECODE_NOENT,
155 bc826292 Michael Hanselmann
  ECODE_EXISTS,
156 bc826292 Michael Hanselmann
  ECODE_NOTUNIQUE,
157 bc826292 Michael Hanselmann
  ECODE_FAULT,
158 bc826292 Michael Hanselmann
  ECODE_ENVIRON,
159 bc826292 Michael Hanselmann
  ])
160 bc826292 Michael Hanselmann
161 2a7c3583 Michael Hanselmann
# Older pycURL versions don't have all error constants
162 2a7c3583 Michael Hanselmann
try:
163 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
164 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
165 2a7c3583 Michael Hanselmann
except AttributeError:
166 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = 60
167 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = 77
168 2a7c3583 Michael Hanselmann
169 2a7c3583 Michael Hanselmann
_CURL_SSL_CERT_ERRORS = frozenset([
170 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT,
171 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE,
172 2a7c3583 Michael Hanselmann
  ])
173 2a7c3583 Michael Hanselmann
174 95ab4de9 David Knowles
175 95ab4de9 David Knowles
class Error(Exception):
176 95ab4de9 David Knowles
  """Base error class for this module.
177 95ab4de9 David Knowles

178 95ab4de9 David Knowles
  """
179 95ab4de9 David Knowles
  pass
180 95ab4de9 David Knowles
181 95ab4de9 David Knowles
182 95ab4de9 David Knowles
class GanetiApiError(Error):
183 95ab4de9 David Knowles
  """Generic error raised from Ganeti API.
184 95ab4de9 David Knowles

185 95ab4de9 David Knowles
  """
186 8a019a03 Michael Hanselmann
  def __init__(self, msg, code=None):
187 8a019a03 Michael Hanselmann
    Error.__init__(self, msg)
188 8a019a03 Michael Hanselmann
    self.code = code
189 95ab4de9 David Knowles
190 95ab4de9 David Knowles
191 4b129313 Chris Schrier
class CertificateError(GanetiApiError):
192 4b129313 Chris Schrier
  """Raised when a problem is found with the SSL certificate.
193 4b129313 Chris Schrier

194 4b129313 Chris Schrier
  """
195 4b129313 Chris Schrier
  pass
196 4b129313 Chris Schrier
197 4b129313 Chris Schrier
198 4c864b55 Michael Hanselmann
def _AppendIf(container, condition, value):
199 4c864b55 Michael Hanselmann
  """Appends to a list if a condition evaluates to truth.
200 4c864b55 Michael Hanselmann

201 4c864b55 Michael Hanselmann
  """
202 4c864b55 Michael Hanselmann
  if condition:
203 4c864b55 Michael Hanselmann
    container.append(value)
204 4c864b55 Michael Hanselmann
205 4c864b55 Michael Hanselmann
  return condition
206 4c864b55 Michael Hanselmann
207 4c864b55 Michael Hanselmann
208 4c864b55 Michael Hanselmann
def _AppendDryRunIf(container, condition):
209 4c864b55 Michael Hanselmann
  """Appends a "dry-run" parameter if a condition evaluates to truth.
210 4c864b55 Michael Hanselmann

211 4c864b55 Michael Hanselmann
  """
212 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_DRY_RUN, 1))
213 4c864b55 Michael Hanselmann
214 4c864b55 Michael Hanselmann
215 4c864b55 Michael Hanselmann
def _AppendForceIf(container, condition):
216 4c864b55 Michael Hanselmann
  """Appends a "force" parameter if a condition evaluates to truth.
217 4c864b55 Michael Hanselmann

218 4c864b55 Michael Hanselmann
  """
219 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_FORCE, 1))
220 4c864b55 Michael Hanselmann
221 4c864b55 Michael Hanselmann
222 57d8e228 Michael Hanselmann
def _SetItemIf(container, condition, item, value):
223 57d8e228 Michael Hanselmann
  """Sets an item if a condition evaluates to truth.
224 57d8e228 Michael Hanselmann

225 57d8e228 Michael Hanselmann
  """
226 57d8e228 Michael Hanselmann
  if condition:
227 57d8e228 Michael Hanselmann
    container[item] = value
228 57d8e228 Michael Hanselmann
229 57d8e228 Michael Hanselmann
  return condition
230 57d8e228 Michael Hanselmann
231 57d8e228 Michael Hanselmann
232 2a7c3583 Michael Hanselmann
def UsesRapiClient(fn):
233 2a7c3583 Michael Hanselmann
  """Decorator for code using RAPI client to initialize pycURL.
234 9279e986 Michael Hanselmann

235 9279e986 Michael Hanselmann
  """
236 2a7c3583 Michael Hanselmann
  def wrapper(*args, **kwargs):
237 2a7c3583 Michael Hanselmann
    # curl_global_init(3) and curl_global_cleanup(3) must be called with only
238 2a7c3583 Michael Hanselmann
    # one thread running. This check is just a safety measure -- it doesn't
239 2a7c3583 Michael Hanselmann
    # cover all cases.
240 2a7c3583 Michael Hanselmann
    assert threading.activeCount() == 1, \
241 2a7c3583 Michael Hanselmann
           "Found active threads when initializing pycURL"
242 2a7c3583 Michael Hanselmann
243 2a7c3583 Michael Hanselmann
    pycurl.global_init(pycurl.GLOBAL_ALL)
244 2a7c3583 Michael Hanselmann
    try:
245 2a7c3583 Michael Hanselmann
      return fn(*args, **kwargs)
246 2a7c3583 Michael Hanselmann
    finally:
247 2a7c3583 Michael Hanselmann
      pycurl.global_cleanup()
248 2a7c3583 Michael Hanselmann
249 2a7c3583 Michael Hanselmann
  return wrapper
250 2a7c3583 Michael Hanselmann
251 2a7c3583 Michael Hanselmann
252 2a7c3583 Michael Hanselmann
def GenericCurlConfig(verbose=False, use_signal=False,
253 2a7c3583 Michael Hanselmann
                      use_curl_cabundle=False, cafile=None, capath=None,
254 2a7c3583 Michael Hanselmann
                      proxy=None, verify_hostname=False,
255 2a7c3583 Michael Hanselmann
                      connect_timeout=None, timeout=None,
256 2a7c3583 Michael Hanselmann
                      _pycurl_version_fn=pycurl.version_info):
257 2a7c3583 Michael Hanselmann
  """Curl configuration function generator.
258 2a7c3583 Michael Hanselmann

259 2a7c3583 Michael Hanselmann
  @type verbose: bool
260 2a7c3583 Michael Hanselmann
  @param verbose: Whether to set cURL to verbose mode
261 2a7c3583 Michael Hanselmann
  @type use_signal: bool
262 2a7c3583 Michael Hanselmann
  @param use_signal: Whether to allow cURL to use signals
263 2a7c3583 Michael Hanselmann
  @type use_curl_cabundle: bool
264 2a7c3583 Michael Hanselmann
  @param use_curl_cabundle: Whether to use cURL's default CA bundle
265 2a7c3583 Michael Hanselmann
  @type cafile: string
266 2a7c3583 Michael Hanselmann
  @param cafile: In which file we can find the certificates
267 2a7c3583 Michael Hanselmann
  @type capath: string
268 2a7c3583 Michael Hanselmann
  @param capath: In which directory we can find the certificates
269 2a7c3583 Michael Hanselmann
  @type proxy: string
270 2a7c3583 Michael Hanselmann
  @param proxy: Proxy to use, None for default behaviour and empty string for
271 2a7c3583 Michael Hanselmann
                disabling proxies (see curl_easy_setopt(3))
272 2a7c3583 Michael Hanselmann
  @type verify_hostname: bool
273 2a7c3583 Michael Hanselmann
  @param verify_hostname: Whether to verify the remote peer certificate's
274 2a7c3583 Michael Hanselmann
                          commonName
275 2a7c3583 Michael Hanselmann
  @type connect_timeout: number
276 2a7c3583 Michael Hanselmann
  @param connect_timeout: Timeout for establishing connection in seconds
277 2a7c3583 Michael Hanselmann
  @type timeout: number
278 2a7c3583 Michael Hanselmann
  @param timeout: Timeout for complete transfer in seconds (see
279 2a7c3583 Michael Hanselmann
                  curl_easy_setopt(3)).
280 9279e986 Michael Hanselmann

281 9279e986 Michael Hanselmann
  """
282 2a7c3583 Michael Hanselmann
  if use_curl_cabundle and (cafile or capath):
283 2a7c3583 Michael Hanselmann
    raise Error("Can not use default CA bundle when CA file or path is set")
284 9279e986 Michael Hanselmann
285 2a7c3583 Michael Hanselmann
  def _ConfigCurl(curl, logger):
286 2a7c3583 Michael Hanselmann
    """Configures a cURL object
287 9279e986 Michael Hanselmann

288 2a7c3583 Michael Hanselmann
    @type curl: pycurl.Curl
289 2a7c3583 Michael Hanselmann
    @param curl: cURL object
290 9279e986 Michael Hanselmann

291 9279e986 Michael Hanselmann
    """
292 2a7c3583 Michael Hanselmann
    logger.debug("Using cURL version %s", pycurl.version)
293 2a7c3583 Michael Hanselmann
294 2a7c3583 Michael Hanselmann
    # pycurl.version_info returns a tuple with information about the used
295 2a7c3583 Michael Hanselmann
    # version of libcurl. Item 5 is the SSL library linked to it.
296 2a7c3583 Michael Hanselmann
    # e.g.: (3, '7.18.0', 463360, 'x86_64-pc-linux-gnu', 1581, 'GnuTLS/2.0.4',
297 2a7c3583 Michael Hanselmann
    # 0, '1.2.3.3', ...)
298 2a7c3583 Michael Hanselmann
    sslver = _pycurl_version_fn()[5]
299 2a7c3583 Michael Hanselmann
    if not sslver:
300 2a7c3583 Michael Hanselmann
      raise Error("No SSL support in cURL")
301 2a7c3583 Michael Hanselmann
302 2a7c3583 Michael Hanselmann
    lcsslver = sslver.lower()
303 2a7c3583 Michael Hanselmann
    if lcsslver.startswith("openssl/"):
304 2a7c3583 Michael Hanselmann
      pass
305 592eb0df Renรฉ Nussbaumer
    elif lcsslver.startswith("nss/"):
306 592eb0df Renรฉ Nussbaumer
      # TODO: investigate compatibility beyond a simple test
307 592eb0df Renรฉ Nussbaumer
      pass
308 2a7c3583 Michael Hanselmann
    elif lcsslver.startswith("gnutls/"):
309 2a7c3583 Michael Hanselmann
      if capath:
310 2a7c3583 Michael Hanselmann
        raise Error("cURL linked against GnuTLS has no support for a"
311 2a7c3583 Michael Hanselmann
                    " CA path (%s)" % (pycurl.version, ))
312 9279e986 Michael Hanselmann
    else:
313 2a7c3583 Michael Hanselmann
      raise NotImplementedError("cURL uses unsupported SSL version '%s'" %
314 2a7c3583 Michael Hanselmann
                                sslver)
315 2a7c3583 Michael Hanselmann
316 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, verbose)
317 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, not use_signal)
318 2a7c3583 Michael Hanselmann
319 2a7c3583 Michael Hanselmann
    # Whether to verify remote peer's CN
320 2a7c3583 Michael Hanselmann
    if verify_hostname:
321 2a7c3583 Michael Hanselmann
      # curl_easy_setopt(3): "When CURLOPT_SSL_VERIFYHOST is 2, that
322 2a7c3583 Michael Hanselmann
      # certificate must indicate that the server is the server to which you
323 2a7c3583 Michael Hanselmann
      # meant to connect, or the connection fails. [...] When the value is 1,
324 2a7c3583 Michael Hanselmann
      # the certificate must contain a Common Name field, but it doesn't matter
325 2a7c3583 Michael Hanselmann
      # what name it says. [...]"
326 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 2)
327 beba56ae Michael Hanselmann
    else:
328 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 0)
329 2a7c3583 Michael Hanselmann
330 2a7c3583 Michael Hanselmann
    if cafile or capath or use_curl_cabundle:
331 2a7c3583 Michael Hanselmann
      # Require certificates to be checked
332 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, True)
333 2a7c3583 Michael Hanselmann
      if cafile:
334 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAINFO, str(cafile))
335 2a7c3583 Michael Hanselmann
      if capath:
336 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAPATH, str(capath))
337 2a7c3583 Michael Hanselmann
      # Not changing anything for using default CA bundle
338 2a7c3583 Michael Hanselmann
    else:
339 2a7c3583 Michael Hanselmann
      # Disable SSL certificate verification
340 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, False)
341 9279e986 Michael Hanselmann
342 2a7c3583 Michael Hanselmann
    if proxy is not None:
343 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.PROXY, str(proxy))
344 9279e986 Michael Hanselmann
345 2a7c3583 Michael Hanselmann
    # Timeouts
346 2a7c3583 Michael Hanselmann
    if connect_timeout is not None:
347 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.CONNECTTIMEOUT, connect_timeout)
348 2a7c3583 Michael Hanselmann
    if timeout is not None:
349 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.TIMEOUT, timeout)
350 9279e986 Michael Hanselmann
351 2a7c3583 Michael Hanselmann
  return _ConfigCurl
352 9279e986 Michael Hanselmann
353 9279e986 Michael Hanselmann
354 b459a848 Andrea Spadaccini
class GanetiRapiClient(object): # pylint: disable=R0904
355 95ab4de9 David Knowles
  """Ganeti RAPI client.
356 95ab4de9 David Knowles

357 95ab4de9 David Knowles
  """
358 95ab4de9 David Knowles
  USER_AGENT = "Ganeti RAPI Client"
359 d3844674 Michael Hanselmann
  _json_encoder = simplejson.JSONEncoder(sort_keys=True)
360 95ab4de9 David Knowles
361 9279e986 Michael Hanselmann
  def __init__(self, host, port=GANETI_RAPI_PORT,
362 2a7c3583 Michael Hanselmann
               username=None, password=None, logger=logging,
363 a5eba783 Michael Hanselmann
               curl_config_fn=None, curl_factory=None):
364 2a7c3583 Michael Hanselmann
    """Initializes this class.
365 95ab4de9 David Knowles

366 9279e986 Michael Hanselmann
    @type host: string
367 9279e986 Michael Hanselmann
    @param host: the ganeti cluster master to interact with
368 95ab4de9 David Knowles
    @type port: int
369 9279e986 Michael Hanselmann
    @param port: the port on which the RAPI is running (default is 5080)
370 9279e986 Michael Hanselmann
    @type username: string
371 95ab4de9 David Knowles
    @param username: the username to connect with
372 9279e986 Michael Hanselmann
    @type password: string
373 95ab4de9 David Knowles
    @param password: the password to connect with
374 2a7c3583 Michael Hanselmann
    @type curl_config_fn: callable
375 2a7c3583 Michael Hanselmann
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
376 9279e986 Michael Hanselmann
    @param logger: Logging object
377 95ab4de9 David Knowles

378 95ab4de9 David Knowles
    """
379 a5eba783 Michael Hanselmann
    self._username = username
380 a5eba783 Michael Hanselmann
    self._password = password
381 9279e986 Michael Hanselmann
    self._logger = logger
382 a5eba783 Michael Hanselmann
    self._curl_config_fn = curl_config_fn
383 a5eba783 Michael Hanselmann
    self._curl_factory = curl_factory
384 95ab4de9 David Knowles
385 1a8337f2 Manuel Franceschini
    try:
386 1a8337f2 Manuel Franceschini
      socket.inet_pton(socket.AF_INET6, host)
387 1a8337f2 Manuel Franceschini
      address = "[%s]:%s" % (host, port)
388 1a8337f2 Manuel Franceschini
    except socket.error:
389 1a8337f2 Manuel Franceschini
      address = "%s:%s" % (host, port)
390 1a8337f2 Manuel Franceschini
391 1a8337f2 Manuel Franceschini
    self._base_url = "https://%s" % address
392 f2f88abf David Knowles
393 a5eba783 Michael Hanselmann
    if username is not None:
394 a5eba783 Michael Hanselmann
      if password is None:
395 a5eba783 Michael Hanselmann
        raise Error("Password not specified")
396 a5eba783 Michael Hanselmann
    elif password:
397 a5eba783 Michael Hanselmann
      raise Error("Specified password without username")
398 a5eba783 Michael Hanselmann
399 a5eba783 Michael Hanselmann
  def _CreateCurl(self):
400 a5eba783 Michael Hanselmann
    """Creates a cURL object.
401 a5eba783 Michael Hanselmann

402 a5eba783 Michael Hanselmann
    """
403 a5eba783 Michael Hanselmann
    # Create pycURL object if no factory is provided
404 a5eba783 Michael Hanselmann
    if self._curl_factory:
405 a5eba783 Michael Hanselmann
      curl = self._curl_factory()
406 a5eba783 Michael Hanselmann
    else:
407 2a7c3583 Michael Hanselmann
      curl = pycurl.Curl()
408 2a7c3583 Michael Hanselmann
409 2a7c3583 Michael Hanselmann
    # Default cURL settings
410 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, False)
411 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.FOLLOWLOCATION, False)
412 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.MAXREDIRS, 5)
413 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, True)
414 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.USERAGENT, self.USER_AGENT)
415 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYHOST, 0)
416 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYPEER, False)
417 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.HTTPHEADER, [
418 2a7c3583 Michael Hanselmann
      "Accept: %s" % HTTP_APP_JSON,
419 2a7c3583 Michael Hanselmann
      "Content-type: %s" % HTTP_APP_JSON,
420 2a7c3583 Michael Hanselmann
      ])
421 2a7c3583 Michael Hanselmann
422 a5eba783 Michael Hanselmann
    assert ((self._username is None and self._password is None) ^
423 a5eba783 Michael Hanselmann
            (self._username is not None and self._password is not None))
424 a5eba783 Michael Hanselmann
425 a5eba783 Michael Hanselmann
    if self._username:
426 a5eba783 Michael Hanselmann
      # Setup authentication
427 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
428 a5eba783 Michael Hanselmann
      curl.setopt(pycurl.USERPWD,
429 a5eba783 Michael Hanselmann
                  str("%s:%s" % (self._username, self._password)))
430 9279e986 Michael Hanselmann
431 2a7c3583 Michael Hanselmann
    # Call external configuration function
432 a5eba783 Michael Hanselmann
    if self._curl_config_fn:
433 a5eba783 Michael Hanselmann
      self._curl_config_fn(curl, self._logger)
434 f2f88abf David Knowles
435 a5eba783 Michael Hanselmann
    return curl
436 95ab4de9 David Knowles
437 10f5ab6c Michael Hanselmann
  @staticmethod
438 10f5ab6c Michael Hanselmann
  def _EncodeQuery(query):
439 10f5ab6c Michael Hanselmann
    """Encode query values for RAPI URL.
440 10f5ab6c Michael Hanselmann

441 10f5ab6c Michael Hanselmann
    @type query: list of two-tuples
442 10f5ab6c Michael Hanselmann
    @param query: Query arguments
443 10f5ab6c Michael Hanselmann
    @rtype: list
444 10f5ab6c Michael Hanselmann
    @return: Query list with encoded values
445 10f5ab6c Michael Hanselmann

446 10f5ab6c Michael Hanselmann
    """
447 10f5ab6c Michael Hanselmann
    result = []
448 10f5ab6c Michael Hanselmann
449 10f5ab6c Michael Hanselmann
    for name, value in query:
450 10f5ab6c Michael Hanselmann
      if value is None:
451 10f5ab6c Michael Hanselmann
        result.append((name, ""))
452 10f5ab6c Michael Hanselmann
453 10f5ab6c Michael Hanselmann
      elif isinstance(value, bool):
454 10f5ab6c Michael Hanselmann
        # Boolean values must be encoded as 0 or 1
455 10f5ab6c Michael Hanselmann
        result.append((name, int(value)))
456 10f5ab6c Michael Hanselmann
457 10f5ab6c Michael Hanselmann
      elif isinstance(value, (list, tuple, dict)):
458 10f5ab6c Michael Hanselmann
        raise ValueError("Invalid query data type %r" % type(value).__name__)
459 10f5ab6c Michael Hanselmann
460 10f5ab6c Michael Hanselmann
      else:
461 10f5ab6c Michael Hanselmann
        result.append((name, value))
462 10f5ab6c Michael Hanselmann
463 10f5ab6c Michael Hanselmann
    return result
464 10f5ab6c Michael Hanselmann
465 768747ed Michael Hanselmann
  def _SendRequest(self, method, path, query, content):
466 95ab4de9 David Knowles
    """Sends an HTTP request.
467 95ab4de9 David Knowles

468 95ab4de9 David Knowles
    This constructs a full URL, encodes and decodes HTTP bodies, and
469 95ab4de9 David Knowles
    handles invalid responses in a pythonic way.
470 95ab4de9 David Knowles

471 768747ed Michael Hanselmann
    @type method: string
472 95ab4de9 David Knowles
    @param method: HTTP method to use
473 768747ed Michael Hanselmann
    @type path: string
474 95ab4de9 David Knowles
    @param path: HTTP URL path
475 95ab4de9 David Knowles
    @type query: list of two-tuples
476 95ab4de9 David Knowles
    @param query: query arguments to pass to urllib.urlencode
477 95ab4de9 David Knowles
    @type content: str or None
478 95ab4de9 David Knowles
    @param content: HTTP body content
479 95ab4de9 David Knowles

480 95ab4de9 David Knowles
    @rtype: str
481 95ab4de9 David Knowles
    @return: JSON-Decoded response
482 95ab4de9 David Knowles

483 f2f88abf David Knowles
    @raises CertificateError: If an invalid SSL certificate is found
484 95ab4de9 David Knowles
    @raises GanetiApiError: If an invalid response is returned
485 95ab4de9 David Knowles

486 95ab4de9 David Knowles
    """
487 ccd6b542 Michael Hanselmann
    assert path.startswith("/")
488 ccd6b542 Michael Hanselmann
489 a5eba783 Michael Hanselmann
    curl = self._CreateCurl()
490 2a7c3583 Michael Hanselmann
491 8306e0e4 Michael Hanselmann
    if content is not None:
492 d3844674 Michael Hanselmann
      encoded_content = self._json_encoder.encode(content)
493 d3844674 Michael Hanselmann
    else:
494 2a7c3583 Michael Hanselmann
      encoded_content = ""
495 95ab4de9 David Knowles
496 ccd6b542 Michael Hanselmann
    # Build URL
497 f961e2ba Michael Hanselmann
    urlparts = [self._base_url, path]
498 ccd6b542 Michael Hanselmann
    if query:
499 f961e2ba Michael Hanselmann
      urlparts.append("?")
500 f961e2ba Michael Hanselmann
      urlparts.append(urllib.urlencode(self._EncodeQuery(query)))
501 9279e986 Michael Hanselmann
502 f961e2ba Michael Hanselmann
    url = "".join(urlparts)
503 f961e2ba Michael Hanselmann
504 a5eba783 Michael Hanselmann
    self._logger.debug("Sending request %s %s (content=%r)",
505 a5eba783 Michael Hanselmann
                       method, url, encoded_content)
506 2a7c3583 Michael Hanselmann
507 2a7c3583 Michael Hanselmann
    # Buffer for response
508 2a7c3583 Michael Hanselmann
    encoded_resp_body = StringIO()
509 f961e2ba Michael Hanselmann
510 2a7c3583 Michael Hanselmann
    # Configure cURL
511 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.CUSTOMREQUEST, str(method))
512 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.URL, str(url))
513 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.POSTFIELDS, str(encoded_content))
514 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.WRITEFUNCTION, encoded_resp_body.write)
515 9279e986 Michael Hanselmann
516 f2f88abf David Knowles
    try:
517 2a7c3583 Michael Hanselmann
      # Send request and wait for response
518 2a7c3583 Michael Hanselmann
      try:
519 2a7c3583 Michael Hanselmann
        curl.perform()
520 2a7c3583 Michael Hanselmann
      except pycurl.error, err:
521 2a7c3583 Michael Hanselmann
        if err.args[0] in _CURL_SSL_CERT_ERRORS:
522 4b129313 Chris Schrier
          raise CertificateError("SSL certificate error %s" % err,
523 4b129313 Chris Schrier
                                 code=err.args[0])
524 2a7c3583 Michael Hanselmann
525 b5800ee9 Chris Schrier
        raise GanetiApiError(str(err), code=err.args[0])
526 2a7c3583 Michael Hanselmann
    finally:
527 2a7c3583 Michael Hanselmann
      # Reset settings to not keep references to large objects in memory
528 2a7c3583 Michael Hanselmann
      # between requests
529 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.POSTFIELDS, "")
530 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.WRITEFUNCTION, lambda _: None)
531 2a7c3583 Michael Hanselmann
532 2a7c3583 Michael Hanselmann
    # Get HTTP response code
533 2a7c3583 Michael Hanselmann
    http_code = curl.getinfo(pycurl.RESPONSE_CODE)
534 2a7c3583 Michael Hanselmann
535 2a7c3583 Michael Hanselmann
    # Was anything written to the response buffer?
536 2a7c3583 Michael Hanselmann
    if encoded_resp_body.tell():
537 2a7c3583 Michael Hanselmann
      response_content = simplejson.loads(encoded_resp_body.getvalue())
538 d3844674 Michael Hanselmann
    else:
539 d3844674 Michael Hanselmann
      response_content = None
540 95ab4de9 David Knowles
541 2a7c3583 Michael Hanselmann
    if http_code != HTTP_OK:
542 d3844674 Michael Hanselmann
      if isinstance(response_content, dict):
543 95ab4de9 David Knowles
        msg = ("%s %s: %s" %
544 d3844674 Michael Hanselmann
               (response_content["code"],
545 d3844674 Michael Hanselmann
                response_content["message"],
546 d3844674 Michael Hanselmann
                response_content["explain"]))
547 95ab4de9 David Knowles
      else:
548 d3844674 Michael Hanselmann
        msg = str(response_content)
549 d3844674 Michael Hanselmann
550 2a7c3583 Michael Hanselmann
      raise GanetiApiError(msg, code=http_code)
551 95ab4de9 David Knowles
552 d3844674 Michael Hanselmann
    return response_content
553 95ab4de9 David Knowles
554 95ab4de9 David Knowles
  def GetVersion(self):
555 cab667cc David Knowles
    """Gets the Remote API version running on the cluster.
556 95ab4de9 David Knowles

557 95ab4de9 David Knowles
    @rtype: int
558 f2f88abf David Knowles
    @return: Ganeti Remote API version
559 95ab4de9 David Knowles

560 95ab4de9 David Knowles
    """
561 768747ed Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/version", None, None)
562 95ab4de9 David Knowles
563 7eac4a4d Michael Hanselmann
  def GetFeatures(self):
564 7eac4a4d Michael Hanselmann
    """Gets the list of optional features supported by RAPI server.
565 7eac4a4d Michael Hanselmann

566 7eac4a4d Michael Hanselmann
    @rtype: list
567 7eac4a4d Michael Hanselmann
    @return: List of optional features
568 7eac4a4d Michael Hanselmann

569 7eac4a4d Michael Hanselmann
    """
570 7eac4a4d Michael Hanselmann
    try:
571 7eac4a4d Michael Hanselmann
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
572 7eac4a4d Michael Hanselmann
                               None, None)
573 7eac4a4d Michael Hanselmann
    except GanetiApiError, err:
574 7eac4a4d Michael Hanselmann
      # Older RAPI servers don't support this resource
575 7eac4a4d Michael Hanselmann
      if err.code == HTTP_NOT_FOUND:
576 7eac4a4d Michael Hanselmann
        return []
577 7eac4a4d Michael Hanselmann
578 7eac4a4d Michael Hanselmann
      raise
579 7eac4a4d Michael Hanselmann
580 95ab4de9 David Knowles
  def GetOperatingSystems(self):
581 95ab4de9 David Knowles
    """Gets the Operating Systems running in the Ganeti cluster.
582 95ab4de9 David Knowles

583 95ab4de9 David Knowles
    @rtype: list of str
584 95ab4de9 David Knowles
    @return: operating systems
585 95ab4de9 David Knowles

586 95ab4de9 David Knowles
    """
587 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
588 a198b2d9 Michael Hanselmann
                             None, None)
589 95ab4de9 David Knowles
590 95ab4de9 David Knowles
  def GetInfo(self):
591 95ab4de9 David Knowles
    """Gets info about the cluster.
592 95ab4de9 David Knowles

593 95ab4de9 David Knowles
    @rtype: dict
594 95ab4de9 David Knowles
    @return: information about the cluster
595 95ab4de9 David Knowles

596 95ab4de9 David Knowles
    """
597 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
598 a198b2d9 Michael Hanselmann
                             None, None)
599 95ab4de9 David Knowles
600 54d4c13b Michael Hanselmann
  def RedistributeConfig(self):
601 54d4c13b Michael Hanselmann
    """Tells the cluster to redistribute its configuration files.
602 54d4c13b Michael Hanselmann

603 d914c76f Simeon Miteff
    @rtype: string
604 54d4c13b Michael Hanselmann
    @return: job id
605 54d4c13b Michael Hanselmann

606 54d4c13b Michael Hanselmann
    """
607 54d4c13b Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
608 54d4c13b Michael Hanselmann
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
609 54d4c13b Michael Hanselmann
                             None, None)
610 54d4c13b Michael Hanselmann
611 62e999a5 Michael Hanselmann
  def ModifyCluster(self, **kwargs):
612 62e999a5 Michael Hanselmann
    """Modifies cluster parameters.
613 62e999a5 Michael Hanselmann

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

616 98805538 Michael Hanselmann
    @rtype: string
617 62e999a5 Michael Hanselmann
    @return: job id
618 62e999a5 Michael Hanselmann

619 62e999a5 Michael Hanselmann
    """
620 62e999a5 Michael Hanselmann
    body = kwargs
621 62e999a5 Michael Hanselmann
622 62e999a5 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
623 62e999a5 Michael Hanselmann
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
624 62e999a5 Michael Hanselmann
625 95ab4de9 David Knowles
  def GetClusterTags(self):
626 95ab4de9 David Knowles
    """Gets the cluster tags.
627 95ab4de9 David Knowles

628 95ab4de9 David Knowles
    @rtype: list of str
629 95ab4de9 David Knowles
    @return: cluster tags
630 95ab4de9 David Knowles

631 95ab4de9 David Knowles
    """
632 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
633 a198b2d9 Michael Hanselmann
                             None, None)
634 95ab4de9 David Knowles
635 95ab4de9 David Knowles
  def AddClusterTags(self, tags, dry_run=False):
636 95ab4de9 David Knowles
    """Adds tags to the cluster.
637 95ab4de9 David Knowles

638 95ab4de9 David Knowles
    @type tags: list of str
639 95ab4de9 David Knowles
    @param tags: tags to add to the cluster
640 95ab4de9 David Knowles
    @type dry_run: bool
641 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
642 95ab4de9 David Knowles

643 98805538 Michael Hanselmann
    @rtype: string
644 95ab4de9 David Knowles
    @return: job id
645 95ab4de9 David Knowles

646 95ab4de9 David Knowles
    """
647 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
648 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
649 95ab4de9 David Knowles
650 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
651 a198b2d9 Michael Hanselmann
                             query, None)
652 95ab4de9 David Knowles
653 95ab4de9 David Knowles
  def DeleteClusterTags(self, tags, dry_run=False):
654 95ab4de9 David Knowles
    """Deletes tags from the cluster.
655 95ab4de9 David Knowles

656 95ab4de9 David Knowles
    @type tags: list of str
657 95ab4de9 David Knowles
    @param tags: tags to delete
658 95ab4de9 David Knowles
    @type dry_run: bool
659 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
660 d914c76f Simeon Miteff
    @rtype: string
661 d914c76f Simeon Miteff
    @return: job id
662 95ab4de9 David Knowles

663 95ab4de9 David Knowles
    """
664 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
665 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
666 95ab4de9 David Knowles
667 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
668 a198b2d9 Michael Hanselmann
                             query, None)
669 95ab4de9 David Knowles
670 95ab4de9 David Knowles
  def GetInstances(self, bulk=False):
671 95ab4de9 David Knowles
    """Gets information about instances on the cluster.
672 95ab4de9 David Knowles

673 95ab4de9 David Knowles
    @type bulk: bool
674 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
675 95ab4de9 David Knowles

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

679 95ab4de9 David Knowles
    """
680 95ab4de9 David Knowles
    query = []
681 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
682 95ab4de9 David Knowles
683 a198b2d9 Michael Hanselmann
    instances = self._SendRequest(HTTP_GET,
684 a198b2d9 Michael Hanselmann
                                  "/%s/instances" % GANETI_RAPI_VERSION,
685 a198b2d9 Michael Hanselmann
                                  query, None)
686 95ab4de9 David Knowles
    if bulk:
687 95ab4de9 David Knowles
      return instances
688 95ab4de9 David Knowles
    else:
689 95ab4de9 David Knowles
      return [i["id"] for i in instances]
690 95ab4de9 David Knowles
691 591e5103 Michael Hanselmann
  def GetInstance(self, instance):
692 95ab4de9 David Knowles
    """Gets information about an instance.
693 95ab4de9 David Knowles

694 95ab4de9 David Knowles
    @type instance: str
695 95ab4de9 David Knowles
    @param instance: instance whose info to return
696 95ab4de9 David Knowles

697 95ab4de9 David Knowles
    @rtype: dict
698 95ab4de9 David Knowles
    @return: info about the instance
699 95ab4de9 David Knowles

700 95ab4de9 David Knowles
    """
701 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
702 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
703 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
704 95ab4de9 David Knowles
705 591e5103 Michael Hanselmann
  def GetInstanceInfo(self, instance, static=None):
706 591e5103 Michael Hanselmann
    """Gets information about an instance.
707 591e5103 Michael Hanselmann

708 591e5103 Michael Hanselmann
    @type instance: string
709 591e5103 Michael Hanselmann
    @param instance: Instance name
710 591e5103 Michael Hanselmann
    @rtype: string
711 591e5103 Michael Hanselmann
    @return: Job ID
712 591e5103 Michael Hanselmann

713 591e5103 Michael Hanselmann
    """
714 591e5103 Michael Hanselmann
    if static is not None:
715 591e5103 Michael Hanselmann
      query = [("static", static)]
716 591e5103 Michael Hanselmann
    else:
717 591e5103 Michael Hanselmann
      query = None
718 591e5103 Michael Hanselmann
719 591e5103 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
720 591e5103 Michael Hanselmann
                             ("/%s/instances/%s/info" %
721 591e5103 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
722 591e5103 Michael Hanselmann
723 915faf72 Renรฉ Nussbaumer
  @staticmethod
724 915faf72 Renรฉ Nussbaumer
  def _UpdateWithKwargs(base, **kwargs):
725 915faf72 Renรฉ Nussbaumer
    """Updates the base with params from kwargs.
726 915faf72 Renรฉ Nussbaumer

727 915faf72 Renรฉ Nussbaumer
    @param base: The base dict, filled with required fields
728 915faf72 Renรฉ Nussbaumer

729 915faf72 Renรฉ Nussbaumer
    @note: This is an inplace update of base
730 915faf72 Renรฉ Nussbaumer

731 915faf72 Renรฉ Nussbaumer
    """
732 915faf72 Renรฉ Nussbaumer
    conflicts = set(kwargs.iterkeys()) & set(base.iterkeys())
733 915faf72 Renรฉ Nussbaumer
    if conflicts:
734 915faf72 Renรฉ Nussbaumer
      raise GanetiApiError("Required fields can not be specified as"
735 915faf72 Renรฉ Nussbaumer
                           " keywords: %s" % ", ".join(conflicts))
736 915faf72 Renรฉ Nussbaumer
737 915faf72 Renรฉ Nussbaumer
    base.update((key, value) for key, value in kwargs.iteritems()
738 915faf72 Renรฉ Nussbaumer
                if key != "dry_run")
739 915faf72 Renรฉ Nussbaumer
740 915faf72 Renรฉ Nussbaumer
  def InstanceAllocation(self, mode, name, disk_template, disks, nics,
741 915faf72 Renรฉ Nussbaumer
                         **kwargs):
742 915faf72 Renรฉ Nussbaumer
    """Generates an instance allocation as used by multiallocate.
743 915faf72 Renรฉ Nussbaumer

744 915faf72 Renรฉ Nussbaumer
    More details for parameters can be found in the RAPI documentation.
745 915faf72 Renรฉ Nussbaumer
    It is the same as used by CreateInstance.
746 915faf72 Renรฉ Nussbaumer

747 915faf72 Renรฉ Nussbaumer
    @type mode: string
748 915faf72 Renรฉ Nussbaumer
    @param mode: Instance creation mode
749 915faf72 Renรฉ Nussbaumer
    @type name: string
750 915faf72 Renรฉ Nussbaumer
    @param name: Hostname of the instance to create
751 915faf72 Renรฉ Nussbaumer
    @type disk_template: string
752 915faf72 Renรฉ Nussbaumer
    @param disk_template: Disk template for instance (e.g. plain, diskless,
753 915faf72 Renรฉ Nussbaumer
                          file, or drbd)
754 915faf72 Renรฉ Nussbaumer
    @type disks: list of dicts
755 915faf72 Renรฉ Nussbaumer
    @param disks: List of disk definitions
756 915faf72 Renรฉ Nussbaumer
    @type nics: list of dicts
757 915faf72 Renรฉ Nussbaumer
    @param nics: List of NIC definitions
758 915faf72 Renรฉ Nussbaumer

759 915faf72 Renรฉ Nussbaumer
    @return: A dict with the generated entry
760 915faf72 Renรฉ Nussbaumer

761 915faf72 Renรฉ Nussbaumer
    """
762 915faf72 Renรฉ Nussbaumer
    # All required fields for request data version 1
763 915faf72 Renรฉ Nussbaumer
    alloc = {
764 915faf72 Renรฉ Nussbaumer
      "mode": mode,
765 915faf72 Renรฉ Nussbaumer
      "name": name,
766 915faf72 Renรฉ Nussbaumer
      "disk_template": disk_template,
767 915faf72 Renรฉ Nussbaumer
      "disks": disks,
768 915faf72 Renรฉ Nussbaumer
      "nics": nics,
769 915faf72 Renรฉ Nussbaumer
      }
770 915faf72 Renรฉ Nussbaumer
771 915faf72 Renรฉ Nussbaumer
    self._UpdateWithKwargs(alloc, **kwargs)
772 915faf72 Renรฉ Nussbaumer
773 915faf72 Renรฉ Nussbaumer
    return alloc
774 915faf72 Renรฉ Nussbaumer
775 915faf72 Renรฉ Nussbaumer
  def InstancesMultiAlloc(self, instances, **kwargs):
776 915faf72 Renรฉ Nussbaumer
    """Tries to allocate multiple instances.
777 915faf72 Renรฉ Nussbaumer

778 915faf72 Renรฉ Nussbaumer
    More details for parameters can be found in the RAPI documentation.
779 915faf72 Renรฉ Nussbaumer

780 915faf72 Renรฉ Nussbaumer
    @param instances: A list of L{InstanceAllocation} results
781 915faf72 Renรฉ Nussbaumer

782 915faf72 Renรฉ Nussbaumer
    """
783 915faf72 Renรฉ Nussbaumer
    query = []
784 915faf72 Renรฉ Nussbaumer
    body = {
785 915faf72 Renรฉ Nussbaumer
      "instances": instances,
786 915faf72 Renรฉ Nussbaumer
      }
787 915faf72 Renรฉ Nussbaumer
    self._UpdateWithKwargs(body, **kwargs)
788 915faf72 Renรฉ Nussbaumer
789 915faf72 Renรฉ Nussbaumer
    _AppendDryRunIf(query, kwargs.get("dry_run"))
790 915faf72 Renรฉ Nussbaumer
791 915faf72 Renรฉ Nussbaumer
    return self._SendRequest(HTTP_POST,
792 915faf72 Renรฉ Nussbaumer
                             "/%s/instances-multi-alloc" % GANETI_RAPI_VERSION,
793 915faf72 Renรฉ Nussbaumer
                             query, body)
794 915faf72 Renรฉ Nussbaumer
795 8a47b447 Michael Hanselmann
  def CreateInstance(self, mode, name, disk_template, disks, nics,
796 8a47b447 Michael Hanselmann
                     **kwargs):
797 95ab4de9 David Knowles
    """Creates a new instance.
798 95ab4de9 David Knowles

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

801 8a47b447 Michael Hanselmann
    @type mode: string
802 8a47b447 Michael Hanselmann
    @param mode: Instance creation mode
803 8a47b447 Michael Hanselmann
    @type name: string
804 8a47b447 Michael Hanselmann
    @param name: Hostname of the instance to create
805 8a47b447 Michael Hanselmann
    @type disk_template: string
806 8a47b447 Michael Hanselmann
    @param disk_template: Disk template for instance (e.g. plain, diskless,
807 8a47b447 Michael Hanselmann
                          file, or drbd)
808 8a47b447 Michael Hanselmann
    @type disks: list of dicts
809 8a47b447 Michael Hanselmann
    @param disks: List of disk definitions
810 8a47b447 Michael Hanselmann
    @type nics: list of dicts
811 8a47b447 Michael Hanselmann
    @param nics: List of NIC definitions
812 95ab4de9 David Knowles
    @type dry_run: bool
813 8a47b447 Michael Hanselmann
    @keyword dry_run: whether to perform a dry run
814 95ab4de9 David Knowles

815 98805538 Michael Hanselmann
    @rtype: string
816 95ab4de9 David Knowles
    @return: job id
817 95ab4de9 David Knowles

818 95ab4de9 David Knowles
    """
819 95ab4de9 David Knowles
    query = []
820 8a47b447 Michael Hanselmann
821 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, kwargs.get("dry_run"))
822 95ab4de9 David Knowles
823 8a47b447 Michael Hanselmann
    if _INST_CREATE_REQV1 in self.GetFeatures():
824 915faf72 Renรฉ Nussbaumer
      body = self.InstanceAllocation(mode, name, disk_template, disks, nics,
825 915faf72 Renรฉ Nussbaumer
                                     **kwargs)
826 915faf72 Renรฉ Nussbaumer
      body[_REQ_DATA_VERSION_FIELD] = 1
827 8a47b447 Michael Hanselmann
    else:
828 9a8ae794 Michael Hanselmann
      raise GanetiApiError("Server does not support new-style (version 1)"
829 9a8ae794 Michael Hanselmann
                           " instance creation requests")
830 8a47b447 Michael Hanselmann
831 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
832 8a47b447 Michael Hanselmann
                             query, body)
833 95ab4de9 David Knowles
834 077892f8 Dimitris Aragiorgis
  def DeleteInstance(self, instance, dry_run=False, **kwargs):
835 95ab4de9 David Knowles
    """Deletes an instance.
836 95ab4de9 David Knowles

837 95ab4de9 David Knowles
    @type instance: str
838 95ab4de9 David Knowles
    @param instance: the instance to delete
839 95ab4de9 David Knowles

840 98805538 Michael Hanselmann
    @rtype: string
841 cab667cc David Knowles
    @return: job id
842 cab667cc David Knowles

843 95ab4de9 David Knowles
    """
844 95ab4de9 David Knowles
    query = []
845 077892f8 Dimitris Aragiorgis
    body = kwargs
846 077892f8 Dimitris Aragiorgis
847 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
848 95ab4de9 David Knowles
849 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
850 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
851 077892f8 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), query, body)
852 95ab4de9 David Knowles
853 3b7158ef Michael Hanselmann
  def ModifyInstance(self, instance, **kwargs):
854 3b7158ef Michael Hanselmann
    """Modifies an instance.
855 3b7158ef Michael Hanselmann

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

858 3b7158ef Michael Hanselmann
    @type instance: string
859 3b7158ef Michael Hanselmann
    @param instance: Instance name
860 98805538 Michael Hanselmann
    @rtype: string
861 3b7158ef Michael Hanselmann
    @return: job id
862 3b7158ef Michael Hanselmann

863 3b7158ef Michael Hanselmann
    """
864 3b7158ef Michael Hanselmann
    body = kwargs
865 3b7158ef Michael Hanselmann
866 3b7158ef Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
867 3b7158ef Michael Hanselmann
                             ("/%s/instances/%s/modify" %
868 3b7158ef Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
869 3b7158ef Michael Hanselmann
870 b680c8be Michael Hanselmann
  def ActivateInstanceDisks(self, instance, ignore_size=None):
871 b680c8be Michael Hanselmann
    """Activates an instance's disks.
872 b680c8be Michael Hanselmann

873 b680c8be Michael Hanselmann
    @type instance: string
874 b680c8be Michael Hanselmann
    @param instance: Instance name
875 b680c8be Michael Hanselmann
    @type ignore_size: bool
876 b680c8be Michael Hanselmann
    @param ignore_size: Whether to ignore recorded size
877 d914c76f Simeon Miteff
    @rtype: string
878 b680c8be Michael Hanselmann
    @return: job id
879 b680c8be Michael Hanselmann

880 b680c8be Michael Hanselmann
    """
881 b680c8be Michael Hanselmann
    query = []
882 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_size, ("ignore_size", 1))
883 b680c8be Michael Hanselmann
884 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
885 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/activate-disks" %
886 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
887 b680c8be Michael Hanselmann
888 b680c8be Michael Hanselmann
  def DeactivateInstanceDisks(self, instance):
889 b680c8be Michael Hanselmann
    """Deactivates an instance's disks.
890 b680c8be Michael Hanselmann

891 b680c8be Michael Hanselmann
    @type instance: string
892 b680c8be Michael Hanselmann
    @param instance: Instance name
893 d914c76f Simeon Miteff
    @rtype: string
894 b680c8be Michael Hanselmann
    @return: job id
895 b680c8be Michael Hanselmann

896 b680c8be Michael Hanselmann
    """
897 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
898 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/deactivate-disks" %
899 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
900 b680c8be Michael Hanselmann
901 a52978c7 Michael Hanselmann
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
902 a52978c7 Michael Hanselmann
    """Recreate an instance's disks.
903 a52978c7 Michael Hanselmann

904 a52978c7 Michael Hanselmann
    @type instance: string
905 a52978c7 Michael Hanselmann
    @param instance: Instance name
906 a52978c7 Michael Hanselmann
    @type disks: list of int
907 a52978c7 Michael Hanselmann
    @param disks: List of disk indexes
908 a52978c7 Michael Hanselmann
    @type nodes: list of string
909 a52978c7 Michael Hanselmann
    @param nodes: New instance nodes, if relocation is desired
910 a52978c7 Michael Hanselmann
    @rtype: string
911 a52978c7 Michael Hanselmann
    @return: job id
912 a52978c7 Michael Hanselmann

913 a52978c7 Michael Hanselmann
    """
914 a52978c7 Michael Hanselmann
    body = {}
915 57d8e228 Michael Hanselmann
    _SetItemIf(body, disks is not None, "disks", disks)
916 57d8e228 Michael Hanselmann
    _SetItemIf(body, nodes is not None, "nodes", nodes)
917 a52978c7 Michael Hanselmann
918 a52978c7 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
919 a52978c7 Michael Hanselmann
                             ("/%s/instances/%s/recreate-disks" %
920 a52978c7 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
921 a52978c7 Michael Hanselmann
922 e23881ed Michael Hanselmann
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
923 e23881ed Michael Hanselmann
    """Grows a disk of an instance.
924 e23881ed Michael Hanselmann

925 e23881ed Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
926 e23881ed Michael Hanselmann

927 e23881ed Michael Hanselmann
    @type instance: string
928 e23881ed Michael Hanselmann
    @param instance: Instance name
929 e23881ed Michael Hanselmann
    @type disk: integer
930 e23881ed Michael Hanselmann
    @param disk: Disk index
931 e23881ed Michael Hanselmann
    @type amount: integer
932 e23881ed Michael Hanselmann
    @param amount: Grow disk by this amount (MiB)
933 e23881ed Michael Hanselmann
    @type wait_for_sync: bool
934 e23881ed Michael Hanselmann
    @param wait_for_sync: Wait for disk to synchronize
935 98805538 Michael Hanselmann
    @rtype: string
936 e23881ed Michael Hanselmann
    @return: job id
937 e23881ed Michael Hanselmann

938 e23881ed Michael Hanselmann
    """
939 e23881ed Michael Hanselmann
    body = {
940 e23881ed Michael Hanselmann
      "amount": amount,
941 e23881ed Michael Hanselmann
      }
942 e23881ed Michael Hanselmann
943 57d8e228 Michael Hanselmann
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
944 e23881ed Michael Hanselmann
945 e23881ed Michael Hanselmann
    return self._SendRequest(HTTP_POST,
946 e23881ed Michael Hanselmann
                             ("/%s/instances/%s/disk/%s/grow" %
947 e23881ed Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance, disk)),
948 e23881ed Michael Hanselmann
                             None, body)
949 e23881ed Michael Hanselmann
950 95ab4de9 David Knowles
  def GetInstanceTags(self, instance):
951 95ab4de9 David Knowles
    """Gets tags for an instance.
952 95ab4de9 David Knowles

953 95ab4de9 David Knowles
    @type instance: str
954 95ab4de9 David Knowles
    @param instance: instance whose tags to return
955 95ab4de9 David Knowles

956 95ab4de9 David Knowles
    @rtype: list of str
957 95ab4de9 David Knowles
    @return: tags for the instance
958 95ab4de9 David Knowles

959 95ab4de9 David Knowles
    """
960 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
961 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
962 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
963 95ab4de9 David Knowles
964 95ab4de9 David Knowles
  def AddInstanceTags(self, instance, tags, dry_run=False):
965 95ab4de9 David Knowles
    """Adds tags to an instance.
966 95ab4de9 David Knowles

967 95ab4de9 David Knowles
    @type instance: str
968 95ab4de9 David Knowles
    @param instance: instance to add tags to
969 95ab4de9 David Knowles
    @type tags: list of str
970 95ab4de9 David Knowles
    @param tags: tags to add to the instance
971 95ab4de9 David Knowles
    @type dry_run: bool
972 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
973 95ab4de9 David Knowles

974 98805538 Michael Hanselmann
    @rtype: string
975 95ab4de9 David Knowles
    @return: job id
976 95ab4de9 David Knowles

977 95ab4de9 David Knowles
    """
978 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
979 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
980 95ab4de9 David Knowles
981 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
982 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
983 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
984 95ab4de9 David Knowles
985 95ab4de9 David Knowles
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
986 95ab4de9 David Knowles
    """Deletes tags from an instance.
987 95ab4de9 David Knowles

988 95ab4de9 David Knowles
    @type instance: str
989 95ab4de9 David Knowles
    @param instance: instance to delete tags from
990 95ab4de9 David Knowles
    @type tags: list of str
991 95ab4de9 David Knowles
    @param tags: tags to delete
992 95ab4de9 David Knowles
    @type dry_run: bool
993 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
994 d914c76f Simeon Miteff
    @rtype: string
995 d914c76f Simeon Miteff
    @return: job id
996 95ab4de9 David Knowles

997 95ab4de9 David Knowles
    """
998 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
999 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1000 95ab4de9 David Knowles
1001 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1002 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
1003 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1004 95ab4de9 David Knowles
1005 95ab4de9 David Knowles
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
1006 077892f8 Dimitris Aragiorgis
                     dry_run=False, reason=None, **kwargs):
1007 95ab4de9 David Knowles
    """Reboots an instance.
1008 95ab4de9 David Knowles

1009 95ab4de9 David Knowles
    @type instance: str
1010 4a90bd4f Michele Tartara
    @param instance: instance to reboot
1011 95ab4de9 David Knowles
    @type reboot_type: str
1012 95ab4de9 David Knowles
    @param reboot_type: one of: hard, soft, full
1013 95ab4de9 David Knowles
    @type ignore_secondaries: bool
1014 95ab4de9 David Knowles
    @param ignore_secondaries: if True, ignores errors for the secondary node
1015 95ab4de9 David Knowles
        while re-assembling disks (in hard-reboot mode only)
1016 95ab4de9 David Knowles
    @type dry_run: bool
1017 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1018 55cec070 Michele Tartara
    @type reason: string
1019 55cec070 Michele Tartara
    @param reason: the reason for the reboot
1020 d914c76f Simeon Miteff
    @rtype: string
1021 d914c76f Simeon Miteff
    @return: job id
1022 95ab4de9 David Knowles

1023 95ab4de9 David Knowles
    """
1024 95ab4de9 David Knowles
    query = []
1025 077892f8 Dimitris Aragiorgis
    body = kwargs
1026 077892f8 Dimitris Aragiorgis
1027 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1028 4c864b55 Michael Hanselmann
    _AppendIf(query, reboot_type, ("type", reboot_type))
1029 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_secondaries is not None,
1030 4c864b55 Michael Hanselmann
              ("ignore_secondaries", ignore_secondaries))
1031 55cec070 Michele Tartara
    _AppendIf(query, reason, ("reason", reason))
1032 95ab4de9 David Knowles
1033 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1034 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reboot" %
1035 077892f8 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), query, body)
1036 95ab4de9 David Knowles
1037 b8481ebf Guido Trotter
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False,
1038 1f350e0f Michele Tartara
                       reason=None, **kwargs):
1039 95ab4de9 David Knowles
    """Shuts down an instance.
1040 95ab4de9 David Knowles

1041 95ab4de9 David Knowles
    @type instance: str
1042 95ab4de9 David Knowles
    @param instance: the instance to shut down
1043 95ab4de9 David Knowles
    @type dry_run: bool
1044 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1045 2ba39b8f Iustin Pop
    @type no_remember: bool
1046 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
1047 1f350e0f Michele Tartara
    @type reason: string
1048 1f350e0f Michele Tartara
    @param reason: the reason for the shutdown
1049 d914c76f Simeon Miteff
    @rtype: string
1050 d914c76f Simeon Miteff
    @return: job id
1051 95ab4de9 David Knowles

1052 95ab4de9 David Knowles
    """
1053 95ab4de9 David Knowles
    query = []
1054 b8481ebf Guido Trotter
    body = kwargs
1055 b8481ebf Guido Trotter
1056 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1057 7db266bb Daniel Krambrock
    _AppendIf(query, no_remember, ("no_remember", 1))
1058 1f350e0f Michele Tartara
    _AppendIf(query, reason, ("reason", reason))
1059 95ab4de9 David Knowles
1060 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1061 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/shutdown" %
1062 b8481ebf Guido Trotter
                              (GANETI_RAPI_VERSION, instance)), query, body)
1063 95ab4de9 David Knowles
1064 1fa6fcba Michele Tartara
  def StartupInstance(self, instance, dry_run=False, no_remember=False,
1065 1fa6fcba Michele Tartara
                      reason=None):
1066 95ab4de9 David Knowles
    """Starts up an instance.
1067 95ab4de9 David Knowles

1068 95ab4de9 David Knowles
    @type instance: str
1069 95ab4de9 David Knowles
    @param instance: the instance to start up
1070 95ab4de9 David Knowles
    @type dry_run: bool
1071 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1072 2ba39b8f Iustin Pop
    @type no_remember: bool
1073 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
1074 1fa6fcba Michele Tartara
    @type reason: string
1075 1fa6fcba Michele Tartara
    @param reason: the reason for the startup
1076 d914c76f Simeon Miteff
    @rtype: string
1077 d914c76f Simeon Miteff
    @return: job id
1078 95ab4de9 David Knowles

1079 95ab4de9 David Knowles
    """
1080 95ab4de9 David Knowles
    query = []
1081 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1082 7db266bb Daniel Krambrock
    _AppendIf(query, no_remember, ("no_remember", 1))
1083 1fa6fcba Michele Tartara
    _AppendIf(query, reason, ("reason", reason))
1084 95ab4de9 David Knowles
1085 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1086 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/startup" %
1087 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1088 95ab4de9 David Knowles
1089 c744425f Michael Hanselmann
  def ReinstallInstance(self, instance, os=None, no_startup=False,
1090 c744425f Michael Hanselmann
                        osparams=None):
1091 95ab4de9 David Knowles
    """Reinstalls an instance.
1092 95ab4de9 David Knowles

1093 95ab4de9 David Knowles
    @type instance: str
1094 fcee9675 David Knowles
    @param instance: The instance to reinstall
1095 fcee9675 David Knowles
    @type os: str or None
1096 fcee9675 David Knowles
    @param os: The operating system to reinstall. If None, the instance's
1097 fcee9675 David Knowles
        current operating system will be installed again
1098 95ab4de9 David Knowles
    @type no_startup: bool
1099 fcee9675 David Knowles
    @param no_startup: Whether to start the instance automatically
1100 d914c76f Simeon Miteff
    @rtype: string
1101 d914c76f Simeon Miteff
    @return: job id
1102 95ab4de9 David Knowles

1103 95ab4de9 David Knowles
    """
1104 c744425f Michael Hanselmann
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
1105 c744425f Michael Hanselmann
      body = {
1106 c744425f Michael Hanselmann
        "start": not no_startup,
1107 c744425f Michael Hanselmann
        }
1108 57d8e228 Michael Hanselmann
      _SetItemIf(body, os is not None, "os", os)
1109 57d8e228 Michael Hanselmann
      _SetItemIf(body, osparams is not None, "osparams", osparams)
1110 c744425f Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1111 c744425f Michael Hanselmann
                               ("/%s/instances/%s/reinstall" %
1112 c744425f Michael Hanselmann
                                (GANETI_RAPI_VERSION, instance)), None, body)
1113 c744425f Michael Hanselmann
1114 c744425f Michael Hanselmann
    # Use old request format
1115 c744425f Michael Hanselmann
    if osparams:
1116 c744425f Michael Hanselmann
      raise GanetiApiError("Server does not support specifying OS parameters"
1117 c744425f Michael Hanselmann
                           " for instance reinstallation")
1118 c744425f Michael Hanselmann
1119 fcee9675 David Knowles
    query = []
1120 4c864b55 Michael Hanselmann
    _AppendIf(query, os, ("os", os))
1121 4c864b55 Michael Hanselmann
    _AppendIf(query, no_startup, ("nostartup", 1))
1122 4c864b55 Michael Hanselmann
1123 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1124 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reinstall" %
1125 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1126 95ab4de9 David Knowles
1127 bfc2002f Michael Hanselmann
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1128 539d65ba Michael Hanselmann
                           remote_node=None, iallocator=None):
1129 95ab4de9 David Knowles
    """Replaces disks on an instance.
1130 95ab4de9 David Knowles

1131 95ab4de9 David Knowles
    @type instance: str
1132 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1133 bfc2002f Michael Hanselmann
    @type disks: list of ints
1134 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1135 95ab4de9 David Knowles
    @type mode: str
1136 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1137 95ab4de9 David Knowles
    @type remote_node: str or None
1138 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1139 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1140 95ab4de9 David Knowles
    @type iallocator: str or None
1141 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1142 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1143 95ab4de9 David Knowles

1144 98805538 Michael Hanselmann
    @rtype: string
1145 95ab4de9 David Knowles
    @return: job id
1146 95ab4de9 David Knowles

1147 95ab4de9 David Knowles
    """
1148 cfc03c54 Michael Hanselmann
    query = [
1149 cfc03c54 Michael Hanselmann
      ("mode", mode),
1150 cfc03c54 Michael Hanselmann
      ]
1151 95ab4de9 David Knowles
1152 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1153 539d65ba Michael Hanselmann
1154 539d65ba Michael Hanselmann
    if disks is not None:
1155 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1156 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1157 bfc2002f Michael Hanselmann
1158 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1159 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1160 bfc2002f Michael Hanselmann
1161 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1162 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1163 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1164 95ab4de9 David Knowles
1165 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1166 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1167 ebeb600f Michael Hanselmann

1168 ebeb600f Michael Hanselmann
    @type instance: string
1169 ebeb600f Michael Hanselmann
    @param instance: Instance name
1170 ebeb600f Michael Hanselmann
    @type mode: string
1171 ebeb600f Michael Hanselmann
    @param mode: Export mode
1172 ebeb600f Michael Hanselmann
    @rtype: string
1173 ebeb600f Michael Hanselmann
    @return: Job ID
1174 ebeb600f Michael Hanselmann

1175 ebeb600f Michael Hanselmann
    """
1176 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1177 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1178 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1179 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1180 ebeb600f Michael Hanselmann
1181 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1182 ebeb600f Michael Hanselmann
                     remove_instance=None,
1183 5c5c73fd Thomas Thrainer
                     x509_key_name=None, destination_x509_ca=None,
1184 5c5c73fd Thomas Thrainer
                     compress=None):
1185 ebeb600f Michael Hanselmann
    """Exports an instance.
1186 ebeb600f Michael Hanselmann

1187 ebeb600f Michael Hanselmann
    @type instance: string
1188 ebeb600f Michael Hanselmann
    @param instance: Instance name
1189 ebeb600f Michael Hanselmann
    @type mode: string
1190 ebeb600f Michael Hanselmann
    @param mode: Export mode
1191 ebeb600f Michael Hanselmann
    @rtype: string
1192 ebeb600f Michael Hanselmann
    @return: Job ID
1193 ebeb600f Michael Hanselmann

1194 ebeb600f Michael Hanselmann
    """
1195 ebeb600f Michael Hanselmann
    body = {
1196 ebeb600f Michael Hanselmann
      "destination": destination,
1197 ebeb600f Michael Hanselmann
      "mode": mode,
1198 ebeb600f Michael Hanselmann
      }
1199 ebeb600f Michael Hanselmann
1200 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1201 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1202 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1203 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1204 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1205 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1206 5c5c73fd Thomas Thrainer
    _SetItemIf(body, compress is not None, "compress", compress)
1207 ebeb600f Michael Hanselmann
1208 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1209 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1210 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1211 ebeb600f Michael Hanselmann
1212 dc700f75 Daniel Krambrock
  def MigrateInstance(self, instance, mode=None, cleanup=None,
1213 dc700f75 Daniel Krambrock
                      target_node=None):
1214 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1215 e0ac6ce6 Michael Hanselmann

1216 e0ac6ce6 Michael Hanselmann
    @type instance: string
1217 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1218 e0ac6ce6 Michael Hanselmann
    @type mode: string
1219 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1220 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1221 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1222 dc700f75 Daniel Krambrock
    @type target_node: string
1223 dc700f75 Daniel Krambrock
    @param target_node: Target Node for externally mirrored instances
1224 d914c76f Simeon Miteff
    @rtype: string
1225 d914c76f Simeon Miteff
    @return: job id
1226 e0ac6ce6 Michael Hanselmann

1227 e0ac6ce6 Michael Hanselmann
    """
1228 e0ac6ce6 Michael Hanselmann
    body = {}
1229 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1230 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1231 dc700f75 Daniel Krambrock
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1232 e0ac6ce6 Michael Hanselmann
1233 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1234 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1235 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1236 e0ac6ce6 Michael Hanselmann
1237 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1238 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1239 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1240 c0a146a1 Michael Hanselmann

1241 c0a146a1 Michael Hanselmann
    @type instance: string
1242 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1243 c0a146a1 Michael Hanselmann
    @type iallocator: string
1244 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1245 c0a146a1 Michael Hanselmann
      shared-storage instances
1246 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1247 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1248 c0a146a1 Michael Hanselmann
    @type target_node: string
1249 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1250 c0a146a1 Michael Hanselmann
    @rtype: string
1251 c0a146a1 Michael Hanselmann
    @return: job id
1252 c0a146a1 Michael Hanselmann

1253 c0a146a1 Michael Hanselmann
    """
1254 c0a146a1 Michael Hanselmann
    body = {}
1255 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1256 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1257 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1258 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1259 c0a146a1 Michael Hanselmann
1260 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1261 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1262 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1263 c0a146a1 Michael Hanselmann
1264 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1265 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1266 d654aae1 Michael Hanselmann

1267 d654aae1 Michael Hanselmann
    @type instance: string
1268 d654aae1 Michael Hanselmann
    @param instance: Instance name
1269 d654aae1 Michael Hanselmann
    @type new_name: string
1270 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1271 d654aae1 Michael Hanselmann
    @type ip_check: bool
1272 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1273 d654aae1 Michael Hanselmann
    @type name_check: bool
1274 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1275 d914c76f Simeon Miteff
    @rtype: string
1276 d914c76f Simeon Miteff
    @return: job id
1277 d654aae1 Michael Hanselmann

1278 d654aae1 Michael Hanselmann
    """
1279 d654aae1 Michael Hanselmann
    body = {
1280 d654aae1 Michael Hanselmann
      "new_name": new_name,
1281 d654aae1 Michael Hanselmann
      }
1282 d654aae1 Michael Hanselmann
1283 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1284 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1285 d654aae1 Michael Hanselmann
1286 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1287 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1288 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1289 d654aae1 Michael Hanselmann
1290 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1291 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1292 b82d4c5e Michael Hanselmann

1293 b82d4c5e Michael Hanselmann
    @type instance: string
1294 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1295 d914c76f Simeon Miteff
    @rtype: dict
1296 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1297 b82d4c5e Michael Hanselmann

1298 b82d4c5e Michael Hanselmann
    """
1299 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1300 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1301 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1302 b82d4c5e Michael Hanselmann
1303 3312709d Leon Handreke
  def GetJobs(self, bulk=False):
1304 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1305 95ab4de9 David Knowles

1306 3312709d Leon Handreke
    @type bulk: bool
1307 3312709d Leon Handreke
    @param bulk: Whether to return detailed information about jobs.
1308 95ab4de9 David Knowles
    @rtype: list of int
1309 3312709d Leon Handreke
    @return: List of job ids for the cluster or list of dicts with detailed
1310 3312709d Leon Handreke
             information about the jobs if bulk parameter was true.
1311 95ab4de9 David Knowles

1312 95ab4de9 David Knowles
    """
1313 3312709d Leon Handreke
    query = []
1314 3312709d Leon Handreke
    _AppendIf(query, bulk, ("bulk", 1))
1315 3312709d Leon Handreke
1316 3312709d Leon Handreke
    if bulk:
1317 3312709d Leon Handreke
      return self._SendRequest(HTTP_GET,
1318 3312709d Leon Handreke
                               "/%s/jobs" % GANETI_RAPI_VERSION,
1319 3312709d Leon Handreke
                               query, None)
1320 3312709d Leon Handreke
    else:
1321 3312709d Leon Handreke
      return [int(j["id"])
1322 3312709d Leon Handreke
              for j in self._SendRequest(HTTP_GET,
1323 3312709d Leon Handreke
                                         "/%s/jobs" % GANETI_RAPI_VERSION,
1324 3312709d Leon Handreke
                                         None, None)]
1325 95ab4de9 David Knowles
1326 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1327 95ab4de9 David Knowles
    """Gets the status of a job.
1328 95ab4de9 David Knowles

1329 98805538 Michael Hanselmann
    @type job_id: string
1330 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1331 95ab4de9 David Knowles

1332 95ab4de9 David Knowles
    @rtype: dict
1333 95ab4de9 David Knowles
    @return: job status
1334 95ab4de9 David Knowles

1335 95ab4de9 David Knowles
    """
1336 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1337 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1338 a198b2d9 Michael Hanselmann
                             None, None)
1339 95ab4de9 David Knowles
1340 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1341 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1342 16c13387 Theo Van Dinter

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

1346 cfda0e48 Iustin Pop
    @type job_id: string
1347 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1348 16c13387 Theo Van Dinter
    @type period: int
1349 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1350 16c13387 Theo Van Dinter
    @type retries: int
1351 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1352 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1353 16c13387 Theo Van Dinter

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

1360 16c13387 Theo Van Dinter
    """
1361 16c13387 Theo Van Dinter
    while retries != 0:
1362 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1363 dde0e97c Michael Hanselmann
1364 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1365 16c13387 Theo Van Dinter
        return True
1366 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1367 dde0e97c Michael Hanselmann
        return False
1368 dde0e97c Michael Hanselmann
1369 dde0e97c Michael Hanselmann
      if period:
1370 dde0e97c Michael Hanselmann
        time.sleep(period)
1371 dde0e97c Michael Hanselmann
1372 16c13387 Theo Van Dinter
      if retries > 0:
1373 16c13387 Theo Van Dinter
        retries -= 1
1374 dde0e97c Michael Hanselmann
1375 16c13387 Theo Van Dinter
    return False
1376 16c13387 Theo Van Dinter
1377 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1378 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1379 d9b67f70 Michael Hanselmann

1380 98805538 Michael Hanselmann
    @type job_id: string
1381 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1382 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1383 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1384 d914c76f Simeon Miteff
    @rtype: dict
1385 d9b67f70 Michael Hanselmann

1386 d9b67f70 Michael Hanselmann
    """
1387 d9b67f70 Michael Hanselmann
    body = {
1388 d9b67f70 Michael Hanselmann
      "fields": fields,
1389 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1390 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1391 d9b67f70 Michael Hanselmann
      }
1392 d9b67f70 Michael Hanselmann
1393 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1394 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1395 a198b2d9 Michael Hanselmann
                             None, body)
1396 d9b67f70 Michael Hanselmann
1397 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1398 cf9ada49 Michael Hanselmann
    """Cancels a job.
1399 95ab4de9 David Knowles

1400 98805538 Michael Hanselmann
    @type job_id: string
1401 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1402 95ab4de9 David Knowles
    @type dry_run: bool
1403 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1404 d914c76f Simeon Miteff
    @rtype: tuple
1405 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1406 95ab4de9 David Knowles

1407 95ab4de9 David Knowles
    """
1408 95ab4de9 David Knowles
    query = []
1409 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1410 95ab4de9 David Knowles
1411 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1412 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1413 a198b2d9 Michael Hanselmann
                             query, None)
1414 95ab4de9 David Knowles
1415 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1416 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1417 95ab4de9 David Knowles

1418 95ab4de9 David Knowles
    @type bulk: bool
1419 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1420 95ab4de9 David Knowles

1421 95ab4de9 David Knowles
    @rtype: list of dict or str
1422 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1423 95ab4de9 David Knowles
        else list of nodes in the cluster
1424 95ab4de9 David Knowles

1425 95ab4de9 David Knowles
    """
1426 95ab4de9 David Knowles
    query = []
1427 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1428 95ab4de9 David Knowles
1429 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1430 a198b2d9 Michael Hanselmann
                              query, None)
1431 95ab4de9 David Knowles
    if bulk:
1432 95ab4de9 David Knowles
      return nodes
1433 95ab4de9 David Knowles
    else:
1434 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1435 95ab4de9 David Knowles
1436 591e5103 Michael Hanselmann
  def GetNode(self, node):
1437 95ab4de9 David Knowles
    """Gets information about a node.
1438 95ab4de9 David Knowles

1439 95ab4de9 David Knowles
    @type node: str
1440 95ab4de9 David Knowles
    @param node: node whose info to return
1441 95ab4de9 David Knowles

1442 95ab4de9 David Knowles
    @rtype: dict
1443 95ab4de9 David Knowles
    @return: info about the node
1444 95ab4de9 David Knowles

1445 95ab4de9 David Knowles
    """
1446 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1447 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1448 a198b2d9 Michael Hanselmann
                             None, None)
1449 95ab4de9 David Knowles
1450 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1451 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1452 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1453 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1454 95ab4de9 David Knowles

1455 95ab4de9 David Knowles
    @type node: str
1456 95ab4de9 David Knowles
    @param node: node to evacuate
1457 95ab4de9 David Knowles
    @type iallocator: str or None
1458 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1459 95ab4de9 David Knowles
    @type remote_node: str
1460 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1461 95ab4de9 David Knowles
    @type dry_run: bool
1462 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1463 941b9309 Iustin Pop
    @type early_release: bool
1464 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1465 0b58db81 Michael Hanselmann
    @type mode: string
1466 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1467 de40437a Michael Hanselmann
    @type accept_old: bool
1468 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1469 de40437a Michael Hanselmann
        results
1470 de40437a Michael Hanselmann

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

1477 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1478 941b9309 Iustin Pop
        specified
1479 95ab4de9 David Knowles

1480 95ab4de9 David Knowles
    """
1481 95ab4de9 David Knowles
    if iallocator and remote_node:
1482 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1483 95ab4de9 David Knowles
1484 cfc03c54 Michael Hanselmann
    query = []
1485 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1486 de40437a Michael Hanselmann
1487 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1488 0b58db81 Michael Hanselmann
      # Server supports body parameters
1489 de40437a Michael Hanselmann
      body = {}
1490 de40437a Michael Hanselmann
1491 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1492 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1493 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1494 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1495 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1496 de40437a Michael Hanselmann
    else:
1497 de40437a Michael Hanselmann
      # Pre-2.5 request format
1498 de40437a Michael Hanselmann
      body = None
1499 de40437a Michael Hanselmann
1500 de40437a Michael Hanselmann
      if not accept_old:
1501 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1502 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1503 de40437a Michael Hanselmann
                             " accept_old)")
1504 de40437a Michael Hanselmann
1505 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1506 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1507 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1508 de40437a Michael Hanselmann
1509 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1510 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1511 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1512 95ab4de9 David Knowles
1513 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1514 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1515 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1516 95ab4de9 David Knowles
1517 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1518 b7a1c816 Michael Hanselmann
                  target_node=None):
1519 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1520 95ab4de9 David Knowles

1521 95ab4de9 David Knowles
    @type node: str
1522 95ab4de9 David Knowles
    @param node: node to migrate
1523 1f334d96 Iustin Pop
    @type mode: string
1524 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1525 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1526 95ab4de9 David Knowles
    @type dry_run: bool
1527 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1528 b7a1c816 Michael Hanselmann
    @type iallocator: string
1529 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1530 b7a1c816 Michael Hanselmann
    @type target_node: string
1531 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1532 95ab4de9 David Knowles

1533 98805538 Michael Hanselmann
    @rtype: string
1534 95ab4de9 David Knowles
    @return: job id
1535 95ab4de9 David Knowles

1536 95ab4de9 David Knowles
    """
1537 95ab4de9 David Knowles
    query = []
1538 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1539 95ab4de9 David Knowles
1540 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1541 b7a1c816 Michael Hanselmann
      body = {}
1542 b7a1c816 Michael Hanselmann
1543 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1544 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1545 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1546 b7a1c816 Michael Hanselmann
1547 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1548 b7a1c816 Michael Hanselmann
1549 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1550 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1551 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1552 b7a1c816 Michael Hanselmann
    else:
1553 b7a1c816 Michael Hanselmann
      # Use old request format
1554 b7a1c816 Michael Hanselmann
      if target_node is not None:
1555 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1556 b7a1c816 Michael Hanselmann
                             " for node migration")
1557 b7a1c816 Michael Hanselmann
1558 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1559 b7a1c816 Michael Hanselmann
1560 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1561 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1562 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1563 95ab4de9 David Knowles
1564 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1565 95ab4de9 David Knowles
    """Gets the current role for a node.
1566 95ab4de9 David Knowles

1567 95ab4de9 David Knowles
    @type node: str
1568 95ab4de9 David Knowles
    @param node: node whose role to return
1569 95ab4de9 David Knowles

1570 95ab4de9 David Knowles
    @rtype: str
1571 95ab4de9 David Knowles
    @return: the current role for a node
1572 95ab4de9 David Knowles

1573 95ab4de9 David Knowles
    """
1574 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1575 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1576 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1577 95ab4de9 David Knowles
1578 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1579 95ab4de9 David Knowles
    """Sets the role for a node.
1580 95ab4de9 David Knowles

1581 95ab4de9 David Knowles
    @type node: str
1582 95ab4de9 David Knowles
    @param node: the node whose role to set
1583 95ab4de9 David Knowles
    @type role: str
1584 95ab4de9 David Knowles
    @param role: the role to set for the node
1585 95ab4de9 David Knowles
    @type force: bool
1586 95ab4de9 David Knowles
    @param force: whether to force the role change
1587 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1588 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1589 8de8e68d Michael Hanselmann
                         if necessary
1590 95ab4de9 David Knowles

1591 98805538 Michael Hanselmann
    @rtype: string
1592 95ab4de9 David Knowles
    @return: job id
1593 95ab4de9 David Knowles

1594 95ab4de9 David Knowles
    """
1595 4c864b55 Michael Hanselmann
    query = []
1596 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1597 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1598 8de8e68d Michael Hanselmann
1599 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1600 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1601 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1602 95ab4de9 David Knowles
1603 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1604 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1605 42d4d8b9 Michael Hanselmann

1606 42d4d8b9 Michael Hanselmann
    @type node: string
1607 42d4d8b9 Michael Hanselmann
    @param node: Node name
1608 42d4d8b9 Michael Hanselmann
    @type force: bool
1609 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1610 42d4d8b9 Michael Hanselmann
    @rtype: string
1611 42d4d8b9 Michael Hanselmann
    @return: job id
1612 42d4d8b9 Michael Hanselmann

1613 42d4d8b9 Michael Hanselmann
    """
1614 4c864b55 Michael Hanselmann
    query = []
1615 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1616 42d4d8b9 Michael Hanselmann
1617 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1618 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1619 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1620 42d4d8b9 Michael Hanselmann
1621 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1622 94497dd1 Michael Hanselmann
    """Modifies a node.
1623 94497dd1 Michael Hanselmann

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

1626 370f2042 Guido Trotter
    @type node: string
1627 370f2042 Guido Trotter
    @param node: Node name
1628 94497dd1 Michael Hanselmann
    @rtype: string
1629 94497dd1 Michael Hanselmann
    @return: job id
1630 94497dd1 Michael Hanselmann

1631 94497dd1 Michael Hanselmann
    """
1632 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1633 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1634 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1635 94497dd1 Michael Hanselmann
1636 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1637 95ab4de9 David Knowles
    """Gets the storage units for a node.
1638 95ab4de9 David Knowles

1639 95ab4de9 David Knowles
    @type node: str
1640 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1641 95ab4de9 David Knowles
    @type storage_type: str
1642 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1643 95ab4de9 David Knowles
    @type output_fields: str
1644 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1645 95ab4de9 David Knowles

1646 98805538 Michael Hanselmann
    @rtype: string
1647 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1648 95ab4de9 David Knowles

1649 95ab4de9 David Knowles
    """
1650 cfc03c54 Michael Hanselmann
    query = [
1651 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1652 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1653 cfc03c54 Michael Hanselmann
      ]
1654 95ab4de9 David Knowles
1655 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1656 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1657 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1658 95ab4de9 David Knowles
1659 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1660 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1661 95ab4de9 David Knowles

1662 95ab4de9 David Knowles
    @type node: str
1663 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1664 95ab4de9 David Knowles
    @type storage_type: str
1665 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1666 95ab4de9 David Knowles
    @type name: str
1667 95ab4de9 David Knowles
    @param name: name of the storage unit
1668 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1669 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1670 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1671 95ab4de9 David Knowles

1672 98805538 Michael Hanselmann
    @rtype: string
1673 95ab4de9 David Knowles
    @return: job id
1674 95ab4de9 David Knowles

1675 95ab4de9 David Knowles
    """
1676 95ab4de9 David Knowles
    query = [
1677 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1678 cfc03c54 Michael Hanselmann
      ("name", name),
1679 cfc03c54 Michael Hanselmann
      ]
1680 cfc03c54 Michael Hanselmann
1681 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1682 fde28316 Michael Hanselmann
1683 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1684 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1685 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1686 95ab4de9 David Knowles
1687 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1688 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1689 95ab4de9 David Knowles

1690 95ab4de9 David Knowles
    @type node: str
1691 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1692 95ab4de9 David Knowles
    @type storage_type: str
1693 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1694 95ab4de9 David Knowles
    @type name: str
1695 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1696 95ab4de9 David Knowles

1697 98805538 Michael Hanselmann
    @rtype: string
1698 95ab4de9 David Knowles
    @return: job id
1699 95ab4de9 David Knowles

1700 95ab4de9 David Knowles
    """
1701 cfc03c54 Michael Hanselmann
    query = [
1702 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1703 cfc03c54 Michael Hanselmann
      ("name", name),
1704 cfc03c54 Michael Hanselmann
      ]
1705 95ab4de9 David Knowles
1706 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1707 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1708 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1709 95ab4de9 David Knowles
1710 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1711 95ab4de9 David Knowles
    """Gets the tags for a node.
1712 95ab4de9 David Knowles

1713 95ab4de9 David Knowles
    @type node: str
1714 95ab4de9 David Knowles
    @param node: node whose tags to return
1715 95ab4de9 David Knowles

1716 95ab4de9 David Knowles
    @rtype: list of str
1717 95ab4de9 David Knowles
    @return: tags for the node
1718 95ab4de9 David Knowles

1719 95ab4de9 David Knowles
    """
1720 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1721 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1722 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1723 95ab4de9 David Knowles
1724 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1725 95ab4de9 David Knowles
    """Adds tags to a node.
1726 95ab4de9 David Knowles

1727 95ab4de9 David Knowles
    @type node: str
1728 95ab4de9 David Knowles
    @param node: node to add tags to
1729 95ab4de9 David Knowles
    @type tags: list of str
1730 95ab4de9 David Knowles
    @param tags: tags to add to the node
1731 95ab4de9 David Knowles
    @type dry_run: bool
1732 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1733 95ab4de9 David Knowles

1734 98805538 Michael Hanselmann
    @rtype: string
1735 95ab4de9 David Knowles
    @return: job id
1736 95ab4de9 David Knowles

1737 95ab4de9 David Knowles
    """
1738 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1739 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1740 95ab4de9 David Knowles
1741 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1742 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1743 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1744 95ab4de9 David Knowles
1745 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1746 95ab4de9 David Knowles
    """Delete tags from a node.
1747 95ab4de9 David Knowles

1748 95ab4de9 David Knowles
    @type node: str
1749 95ab4de9 David Knowles
    @param node: node to remove tags from
1750 95ab4de9 David Knowles
    @type tags: list of str
1751 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1752 95ab4de9 David Knowles
    @type dry_run: bool
1753 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1754 95ab4de9 David Knowles

1755 98805538 Michael Hanselmann
    @rtype: string
1756 95ab4de9 David Knowles
    @return: job id
1757 95ab4de9 David Knowles

1758 95ab4de9 David Knowles
    """
1759 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1760 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1761 95ab4de9 David Knowles
1762 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1763 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1764 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1765 a268af8d Adeodato Simo
1766 4588b4bd Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1767 4588b4bd Dimitris Aragiorgis
    """Gets all networks in the cluster.
1768 4588b4bd Dimitris Aragiorgis

1769 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1770 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1771 4588b4bd Dimitris Aragiorgis

1772 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1773 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1774 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1775 4588b4bd Dimitris Aragiorgis

1776 4588b4bd Dimitris Aragiorgis
    """
1777 4588b4bd Dimitris Aragiorgis
    query = []
1778 4588b4bd Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1779 4588b4bd Dimitris Aragiorgis
1780 4588b4bd Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1781 3c286190 Dimitris Aragiorgis
                                 query, None)
1782 4588b4bd Dimitris Aragiorgis
    if bulk:
1783 4588b4bd Dimitris Aragiorgis
      return networks
1784 4588b4bd Dimitris Aragiorgis
    else:
1785 4588b4bd Dimitris Aragiorgis
      return [n["name"] for n in networks]
1786 4588b4bd Dimitris Aragiorgis
1787 4588b4bd Dimitris Aragiorgis
  def GetNetwork(self, network):
1788 4588b4bd Dimitris Aragiorgis
    """Gets information about a network.
1789 4588b4bd Dimitris Aragiorgis

1790 1fa2c40b Dimitris Aragiorgis
    @type network: str
1791 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1792 4588b4bd Dimitris Aragiorgis

1793 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1794 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1795 4588b4bd Dimitris Aragiorgis

1796 4588b4bd Dimitris Aragiorgis
    """
1797 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1798 4588b4bd Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1799 4588b4bd Dimitris Aragiorgis
                             None, None)
1800 4588b4bd Dimitris Aragiorgis
1801 4588b4bd Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1802 5cfa6c37 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None,
1803 8140e24f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=None, dry_run=False):
1804 4588b4bd Dimitris Aragiorgis
    """Creates a new network.
1805 4588b4bd Dimitris Aragiorgis

1806 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1807 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1808 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1809 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1810 4588b4bd Dimitris Aragiorgis

1811 4588b4bd Dimitris Aragiorgis
    @rtype: string
1812 4588b4bd Dimitris Aragiorgis
    @return: job id
1813 4588b4bd Dimitris Aragiorgis

1814 4588b4bd Dimitris Aragiorgis
    """
1815 4588b4bd Dimitris Aragiorgis
    query = []
1816 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1817 4588b4bd Dimitris Aragiorgis
1818 8140e24f Dimitris Aragiorgis
    if add_reserved_ips:
1819 2556424d Iustin Pop
      add_reserved_ips = add_reserved_ips.split(",")
1820 8140e24f Dimitris Aragiorgis
1821 8140e24f Dimitris Aragiorgis
    if tags:
1822 2556424d Iustin Pop
      tags = tags.split(",")
1823 8140e24f Dimitris Aragiorgis
1824 4588b4bd Dimitris Aragiorgis
    body = {
1825 4588b4bd Dimitris Aragiorgis
      "network_name": network_name,
1826 4588b4bd Dimitris Aragiorgis
      "gateway": gateway,
1827 4588b4bd Dimitris Aragiorgis
      "network": network,
1828 4588b4bd Dimitris Aragiorgis
      "gateway6": gateway6,
1829 4588b4bd Dimitris Aragiorgis
      "network6": network6,
1830 4588b4bd Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1831 8140e24f Dimitris Aragiorgis
      "add_reserved_ips": add_reserved_ips,
1832 8140e24f Dimitris Aragiorgis
      "tags": tags,
1833 4588b4bd Dimitris Aragiorgis
      }
1834 4588b4bd Dimitris Aragiorgis
1835 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1836 4588b4bd Dimitris Aragiorgis
                             query, body)
1837 4588b4bd Dimitris Aragiorgis
1838 6e8091f9 Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link, dry_run=False):
1839 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1840 4588b4bd Dimitris Aragiorgis

1841 4588b4bd Dimitris Aragiorgis
    """
1842 4588b4bd Dimitris Aragiorgis
    body = {
1843 4588b4bd Dimitris Aragiorgis
      "group_name": group_name,
1844 4588b4bd Dimitris Aragiorgis
      "network_mode": mode,
1845 3c286190 Dimitris Aragiorgis
      "network_link": link,
1846 4588b4bd Dimitris Aragiorgis
      }
1847 4588b4bd Dimitris Aragiorgis
1848 6e8091f9 Dimitris Aragiorgis
    query = []
1849 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1850 6e8091f9 Dimitris Aragiorgis
1851 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1852 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1853 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1854 4588b4bd Dimitris Aragiorgis
1855 6e8091f9 Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name, dry_run=False):
1856 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1857 4588b4bd Dimitris Aragiorgis

1858 4588b4bd Dimitris Aragiorgis
    """
1859 4588b4bd Dimitris Aragiorgis
    body = {
1860 3c286190 Dimitris Aragiorgis
      "group_name": group_name,
1861 4588b4bd Dimitris Aragiorgis
      }
1862 6e8091f9 Dimitris Aragiorgis
1863 6e8091f9 Dimitris Aragiorgis
    query = []
1864 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1865 6e8091f9 Dimitris Aragiorgis
1866 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1867 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1868 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1869 6e8091f9 Dimitris Aragiorgis
1870 6e8091f9 Dimitris Aragiorgis
  def ModifyNetwork(self, network, **kwargs):
1871 6e8091f9 Dimitris Aragiorgis
    """Modifies a network.
1872 6e8091f9 Dimitris Aragiorgis

1873 6e8091f9 Dimitris Aragiorgis
    More details for parameters can be found in the RAPI documentation.
1874 6e8091f9 Dimitris Aragiorgis

1875 6e8091f9 Dimitris Aragiorgis
    @type network: string
1876 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1877 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1878 6e8091f9 Dimitris Aragiorgis
    @return: job id
1879 4588b4bd Dimitris Aragiorgis

1880 6e8091f9 Dimitris Aragiorgis
    """
1881 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1882 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1883 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1884 4588b4bd Dimitris Aragiorgis
1885 4588b4bd Dimitris Aragiorgis
  def DeleteNetwork(self, network, dry_run=False):
1886 4588b4bd Dimitris Aragiorgis
    """Deletes a network.
1887 4588b4bd Dimitris Aragiorgis

1888 1fa2c40b Dimitris Aragiorgis
    @type network: str
1889 1fa2c40b Dimitris Aragiorgis
    @param network: the network to delete
1890 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1891 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1892 4588b4bd Dimitris Aragiorgis

1893 4588b4bd Dimitris Aragiorgis
    @rtype: string
1894 4588b4bd Dimitris Aragiorgis
    @return: job id
1895 4588b4bd Dimitris Aragiorgis

1896 4588b4bd Dimitris Aragiorgis
    """
1897 4588b4bd Dimitris Aragiorgis
    query = []
1898 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1899 4588b4bd Dimitris Aragiorgis
1900 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1901 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1902 4588b4bd Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1903 4588b4bd Dimitris Aragiorgis
1904 6e8091f9 Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1905 6e8091f9 Dimitris Aragiorgis
    """Gets tags for a network.
1906 6e8091f9 Dimitris Aragiorgis

1907 6e8091f9 Dimitris Aragiorgis
    @type network: string
1908 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1909 6e8091f9 Dimitris Aragiorgis

1910 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1911 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1912 6e8091f9 Dimitris Aragiorgis

1913 6e8091f9 Dimitris Aragiorgis
    """
1914 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1915 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1916 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1917 6e8091f9 Dimitris Aragiorgis
1918 6e8091f9 Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1919 6e8091f9 Dimitris Aragiorgis
    """Adds tags to a network.
1920 6e8091f9 Dimitris Aragiorgis

1921 6e8091f9 Dimitris Aragiorgis
    @type network: str
1922 6e8091f9 Dimitris Aragiorgis
    @param network: network to add tags to
1923 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1924 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to add to the network
1925 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1926 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1927 6e8091f9 Dimitris Aragiorgis

1928 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1929 6e8091f9 Dimitris Aragiorgis
    @return: job id
1930 6e8091f9 Dimitris Aragiorgis

1931 6e8091f9 Dimitris Aragiorgis
    """
1932 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1933 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1934 6e8091f9 Dimitris Aragiorgis
1935 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1936 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1937 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1938 6e8091f9 Dimitris Aragiorgis
1939 6e8091f9 Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1940 6e8091f9 Dimitris Aragiorgis
    """Deletes tags from a network.
1941 6e8091f9 Dimitris Aragiorgis

1942 6e8091f9 Dimitris Aragiorgis
    @type network: str
1943 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1944 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1945 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
1946 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1947 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1948 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1949 6e8091f9 Dimitris Aragiorgis
    @return: job id
1950 6e8091f9 Dimitris Aragiorgis

1951 6e8091f9 Dimitris Aragiorgis
    """
1952 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1953 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1954 6e8091f9 Dimitris Aragiorgis
1955 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1956 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1957 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1958 6e8091f9 Dimitris Aragiorgis
1959 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1960 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1961 a268af8d Adeodato Simo

1962 a268af8d Adeodato Simo
    @type bulk: bool
1963 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1964 a268af8d Adeodato Simo

1965 a268af8d Adeodato Simo
    @rtype: list of dict or str
1966 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1967 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1968 a268af8d Adeodato Simo

1969 a268af8d Adeodato Simo
    """
1970 a268af8d Adeodato Simo
    query = []
1971 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1972 a268af8d Adeodato Simo
1973 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1974 a268af8d Adeodato Simo
                               query, None)
1975 a268af8d Adeodato Simo
    if bulk:
1976 a268af8d Adeodato Simo
      return groups
1977 a268af8d Adeodato Simo
    else:
1978 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1979 a268af8d Adeodato Simo
1980 a268af8d Adeodato Simo
  def GetGroup(self, group):
1981 a268af8d Adeodato Simo
    """Gets information about a node group.
1982 a268af8d Adeodato Simo

1983 a268af8d Adeodato Simo
    @type group: str
1984 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1985 a268af8d Adeodato Simo

1986 a268af8d Adeodato Simo
    @rtype: dict
1987 a268af8d Adeodato Simo
    @return: info about the node group
1988 a268af8d Adeodato Simo

1989 a268af8d Adeodato Simo
    """
1990 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1991 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1992 a268af8d Adeodato Simo
                             None, None)
1993 a268af8d Adeodato Simo
1994 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1995 a268af8d Adeodato Simo
    """Creates a new node group.
1996 a268af8d Adeodato Simo

1997 a268af8d Adeodato Simo
    @type name: str
1998 a268af8d Adeodato Simo
    @param name: the name of node group to create
1999 90e99856 Adeodato Simo
    @type alloc_policy: str
2000 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
2001 a268af8d Adeodato Simo
    @type dry_run: bool
2002 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2003 a268af8d Adeodato Simo

2004 98805538 Michael Hanselmann
    @rtype: string
2005 a268af8d Adeodato Simo
    @return: job id
2006 a268af8d Adeodato Simo

2007 a268af8d Adeodato Simo
    """
2008 a268af8d Adeodato Simo
    query = []
2009 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2010 a268af8d Adeodato Simo
2011 a268af8d Adeodato Simo
    body = {
2012 a268af8d Adeodato Simo
      "name": name,
2013 3c286190 Dimitris Aragiorgis
      "alloc_policy": alloc_policy,
2014 a268af8d Adeodato Simo
      }
2015 a268af8d Adeodato Simo
2016 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
2017 a268af8d Adeodato Simo
                             query, body)
2018 a268af8d Adeodato Simo
2019 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
2020 f18fab7d Adeodato Simo
    """Modifies a node group.
2021 f18fab7d Adeodato Simo

2022 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
2023 f18fab7d Adeodato Simo

2024 f18fab7d Adeodato Simo
    @type group: string
2025 f18fab7d Adeodato Simo
    @param group: Node group name
2026 98805538 Michael Hanselmann
    @rtype: string
2027 f18fab7d Adeodato Simo
    @return: job id
2028 f18fab7d Adeodato Simo

2029 f18fab7d Adeodato Simo
    """
2030 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2031 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
2032 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
2033 f18fab7d Adeodato Simo
2034 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
2035 a268af8d Adeodato Simo
    """Deletes a node group.
2036 a268af8d Adeodato Simo

2037 a268af8d Adeodato Simo
    @type group: str
2038 a268af8d Adeodato Simo
    @param group: the node group to delete
2039 a268af8d Adeodato Simo
    @type dry_run: bool
2040 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2041 a268af8d Adeodato Simo

2042 98805538 Michael Hanselmann
    @rtype: string
2043 a268af8d Adeodato Simo
    @return: job id
2044 a268af8d Adeodato Simo

2045 a268af8d Adeodato Simo
    """
2046 a268af8d Adeodato Simo
    query = []
2047 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2048 a268af8d Adeodato Simo
2049 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
2050 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
2051 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
2052 a268af8d Adeodato Simo
2053 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
2054 a268af8d Adeodato Simo
    """Changes the name of a node group.
2055 a268af8d Adeodato Simo

2056 a268af8d Adeodato Simo
    @type group: string
2057 a268af8d Adeodato Simo
    @param group: Node group name
2058 a268af8d Adeodato Simo
    @type new_name: string
2059 a268af8d Adeodato Simo
    @param new_name: New node group name
2060 a268af8d Adeodato Simo

2061 98805538 Michael Hanselmann
    @rtype: string
2062 a268af8d Adeodato Simo
    @return: job id
2063 a268af8d Adeodato Simo

2064 a268af8d Adeodato Simo
    """
2065 a268af8d Adeodato Simo
    body = {
2066 a268af8d Adeodato Simo
      "new_name": new_name,
2067 a268af8d Adeodato Simo
      }
2068 a268af8d Adeodato Simo
2069 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2070 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
2071 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
2072 4245446f Adeodato Simo
2073 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
2074 4245446f Adeodato Simo
    """Assigns nodes to a group.
2075 4245446f Adeodato Simo

2076 4245446f Adeodato Simo
    @type group: string
2077 6ce90543 Michael Hanselmann
    @param group: Node group name
2078 4245446f Adeodato Simo
    @type nodes: list of strings
2079 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
2080 4245446f Adeodato Simo

2081 98805538 Michael Hanselmann
    @rtype: string
2082 4245446f Adeodato Simo
    @return: job id
2083 4245446f Adeodato Simo

2084 4245446f Adeodato Simo
    """
2085 4245446f Adeodato Simo
    query = []
2086 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
2087 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2088 4245446f Adeodato Simo
2089 4245446f Adeodato Simo
    body = {
2090 4245446f Adeodato Simo
      "nodes": nodes,
2091 4245446f Adeodato Simo
      }
2092 4245446f Adeodato Simo
2093 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2094 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
2095 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
2096 208a6cff Michael Hanselmann
2097 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
2098 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
2099 414ebaf1 Michael Hanselmann

2100 414ebaf1 Michael Hanselmann
    @type group: string
2101 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2102 414ebaf1 Michael Hanselmann

2103 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2104 414ebaf1 Michael Hanselmann
    @return: tags for the group
2105 414ebaf1 Michael Hanselmann

2106 414ebaf1 Michael Hanselmann
    """
2107 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2108 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2109 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
2110 414ebaf1 Michael Hanselmann
2111 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
2112 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
2113 414ebaf1 Michael Hanselmann

2114 414ebaf1 Michael Hanselmann
    @type group: str
2115 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
2116 414ebaf1 Michael Hanselmann
    @type tags: list of string
2117 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
2118 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2119 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2120 414ebaf1 Michael Hanselmann

2121 414ebaf1 Michael Hanselmann
    @rtype: string
2122 414ebaf1 Michael Hanselmann
    @return: job id
2123 414ebaf1 Michael Hanselmann

2124 414ebaf1 Michael Hanselmann
    """
2125 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2126 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2127 414ebaf1 Michael Hanselmann
2128 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2129 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2130 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2131 414ebaf1 Michael Hanselmann
2132 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
2133 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
2134 414ebaf1 Michael Hanselmann

2135 414ebaf1 Michael Hanselmann
    @type group: str
2136 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2137 414ebaf1 Michael Hanselmann
    @type tags: list of string
2138 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
2139 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2140 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2141 414ebaf1 Michael Hanselmann
    @rtype: string
2142 414ebaf1 Michael Hanselmann
    @return: job id
2143 414ebaf1 Michael Hanselmann

2144 414ebaf1 Michael Hanselmann
    """
2145 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2146 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2147 414ebaf1 Michael Hanselmann
2148 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
2149 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2150 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2151 414ebaf1 Michael Hanselmann
2152 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
2153 208a6cff Michael Hanselmann
    """Retrieves information about resources.
2154 208a6cff Michael Hanselmann

2155 208a6cff Michael Hanselmann
    @type what: string
2156 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2157 208a6cff Michael Hanselmann
    @type fields: list of string
2158 208a6cff Michael Hanselmann
    @param fields: Requested fields
2159 2e5c33db Iustin Pop
    @type qfilter: None or list
2160 2e5c33db Iustin Pop
    @param qfilter: Query filter
2161 208a6cff Michael Hanselmann

2162 208a6cff Michael Hanselmann
    @rtype: string
2163 208a6cff Michael Hanselmann
    @return: job id
2164 208a6cff Michael Hanselmann

2165 208a6cff Michael Hanselmann
    """
2166 208a6cff Michael Hanselmann
    body = {
2167 208a6cff Michael Hanselmann
      "fields": fields,
2168 208a6cff Michael Hanselmann
      }
2169 208a6cff Michael Hanselmann
2170 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
2171 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
2172 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
2173 208a6cff Michael Hanselmann
2174 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2175 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
2176 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
2177 208a6cff Michael Hanselmann
2178 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
2179 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
2180 208a6cff Michael Hanselmann

2181 208a6cff Michael Hanselmann
    @type what: string
2182 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2183 208a6cff Michael Hanselmann
    @type fields: list of string
2184 208a6cff Michael Hanselmann
    @param fields: Requested fields
2185 208a6cff Michael Hanselmann

2186 208a6cff Michael Hanselmann
    @rtype: string
2187 208a6cff Michael Hanselmann
    @return: job id
2188 208a6cff Michael Hanselmann

2189 208a6cff Michael Hanselmann
    """
2190 208a6cff Michael Hanselmann
    query = []
2191 208a6cff Michael Hanselmann
2192 208a6cff Michael Hanselmann
    if fields is not None:
2193 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
2194 208a6cff Michael Hanselmann
2195 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2196 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
2197 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)