Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 364c350f

History | View | Annotate | Download (63.6 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 95ab4de9 David Knowles
  def DeleteInstance(self, instance, dry_run=False):
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 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
846 95ab4de9 David Knowles
847 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
848 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
849 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
850 95ab4de9 David Knowles
851 3b7158ef Michael Hanselmann
  def ModifyInstance(self, instance, **kwargs):
852 3b7158ef Michael Hanselmann
    """Modifies an instance.
853 3b7158ef Michael Hanselmann

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

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

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

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

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

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

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

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

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

923 e23881ed Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
924 e23881ed Michael Hanselmann

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

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

951 95ab4de9 David Knowles
    @type instance: str
952 95ab4de9 David Knowles
    @param instance: instance whose tags to return
953 95ab4de9 David Knowles

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

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

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

972 98805538 Michael Hanselmann
    @rtype: string
973 95ab4de9 David Knowles
    @return: job id
974 95ab4de9 David Knowles

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

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

995 95ab4de9 David Knowles
    """
996 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
997 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
998 95ab4de9 David Knowles
999 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1000 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
1001 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1002 95ab4de9 David Knowles
1003 95ab4de9 David Knowles
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
1004 55cec070 Michele Tartara
                     dry_run=False, reason=None):
1005 95ab4de9 David Knowles
    """Reboots an instance.
1006 95ab4de9 David Knowles

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

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

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

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

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

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

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

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

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

1140 98805538 Michael Hanselmann
    @rtype: string
1141 95ab4de9 David Knowles
    @return: job id
1142 95ab4de9 David Knowles

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

1164 ebeb600f Michael Hanselmann
    @type instance: string
1165 ebeb600f Michael Hanselmann
    @param instance: Instance name
1166 ebeb600f Michael Hanselmann
    @type mode: string
1167 ebeb600f Michael Hanselmann
    @param mode: Export mode
1168 ebeb600f Michael Hanselmann
    @rtype: string
1169 ebeb600f Michael Hanselmann
    @return: Job ID
1170 ebeb600f Michael Hanselmann

1171 ebeb600f Michael Hanselmann
    """
1172 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1173 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1174 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1175 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1176 ebeb600f Michael Hanselmann
1177 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1178 ebeb600f Michael Hanselmann
                     remove_instance=None,
1179 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1180 ebeb600f Michael Hanselmann
    """Exports an instance.
1181 ebeb600f Michael Hanselmann

1182 ebeb600f Michael Hanselmann
    @type instance: string
1183 ebeb600f Michael Hanselmann
    @param instance: Instance name
1184 ebeb600f Michael Hanselmann
    @type mode: string
1185 ebeb600f Michael Hanselmann
    @param mode: Export mode
1186 ebeb600f Michael Hanselmann
    @rtype: string
1187 ebeb600f Michael Hanselmann
    @return: Job ID
1188 ebeb600f Michael Hanselmann

1189 ebeb600f Michael Hanselmann
    """
1190 ebeb600f Michael Hanselmann
    body = {
1191 ebeb600f Michael Hanselmann
      "destination": destination,
1192 ebeb600f Michael Hanselmann
      "mode": mode,
1193 ebeb600f Michael Hanselmann
      }
1194 ebeb600f Michael Hanselmann
1195 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1196 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1197 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1198 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1199 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1200 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1201 ebeb600f Michael Hanselmann
1202 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1203 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1204 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1205 ebeb600f Michael Hanselmann
1206 dc700f75 Daniel Krambrock
  def MigrateInstance(self, instance, mode=None, cleanup=None,
1207 dc700f75 Daniel Krambrock
                      target_node=None):
1208 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1209 e0ac6ce6 Michael Hanselmann

1210 e0ac6ce6 Michael Hanselmann
    @type instance: string
1211 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1212 e0ac6ce6 Michael Hanselmann
    @type mode: string
1213 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1214 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1215 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1216 dc700f75 Daniel Krambrock
    @type target_node: string
1217 dc700f75 Daniel Krambrock
    @param target_node: Target Node for externally mirrored instances
1218 d914c76f Simeon Miteff
    @rtype: string
1219 d914c76f Simeon Miteff
    @return: job id
1220 e0ac6ce6 Michael Hanselmann

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

1235 c0a146a1 Michael Hanselmann
    @type instance: string
1236 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1237 c0a146a1 Michael Hanselmann
    @type iallocator: string
1238 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1239 c0a146a1 Michael Hanselmann
      shared-storage instances
1240 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1241 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1242 c0a146a1 Michael Hanselmann
    @type target_node: string
1243 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1244 c0a146a1 Michael Hanselmann
    @rtype: string
1245 c0a146a1 Michael Hanselmann
    @return: job id
1246 c0a146a1 Michael Hanselmann

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

1261 d654aae1 Michael Hanselmann
    @type instance: string
1262 d654aae1 Michael Hanselmann
    @param instance: Instance name
1263 d654aae1 Michael Hanselmann
    @type new_name: string
1264 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1265 d654aae1 Michael Hanselmann
    @type ip_check: bool
1266 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1267 d654aae1 Michael Hanselmann
    @type name_check: bool
1268 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1269 d914c76f Simeon Miteff
    @rtype: string
1270 d914c76f Simeon Miteff
    @return: job id
1271 d654aae1 Michael Hanselmann

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

1287 b82d4c5e Michael Hanselmann
    @type instance: string
1288 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1289 d914c76f Simeon Miteff
    @rtype: dict
1290 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1291 b82d4c5e Michael Hanselmann

1292 b82d4c5e Michael Hanselmann
    """
1293 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1294 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1295 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1296 b82d4c5e Michael Hanselmann
1297 3312709d Leon Handreke
  def GetJobs(self, bulk=False):
1298 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1299 95ab4de9 David Knowles

1300 3312709d Leon Handreke
    @type bulk: bool
1301 3312709d Leon Handreke
    @param bulk: Whether to return detailed information about jobs.
1302 95ab4de9 David Knowles
    @rtype: list of int
1303 3312709d Leon Handreke
    @return: List of job ids for the cluster or list of dicts with detailed
1304 3312709d Leon Handreke
             information about the jobs if bulk parameter was true.
1305 95ab4de9 David Knowles

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

1323 98805538 Michael Hanselmann
    @type job_id: string
1324 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1325 95ab4de9 David Knowles

1326 95ab4de9 David Knowles
    @rtype: dict
1327 95ab4de9 David Knowles
    @return: job status
1328 95ab4de9 David Knowles

1329 95ab4de9 David Knowles
    """
1330 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1331 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1332 a198b2d9 Michael Hanselmann
                             None, None)
1333 95ab4de9 David Knowles
1334 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1335 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1336 16c13387 Theo Van Dinter

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

1340 cfda0e48 Iustin Pop
    @type job_id: string
1341 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1342 16c13387 Theo Van Dinter
    @type period: int
1343 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1344 16c13387 Theo Van Dinter
    @type retries: int
1345 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1346 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1347 16c13387 Theo Van Dinter

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

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

1374 98805538 Michael Hanselmann
    @type job_id: string
1375 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1376 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1377 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1378 d914c76f Simeon Miteff
    @rtype: dict
1379 d9b67f70 Michael Hanselmann

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

1394 98805538 Michael Hanselmann
    @type job_id: string
1395 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1396 95ab4de9 David Knowles
    @type dry_run: bool
1397 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1398 d914c76f Simeon Miteff
    @rtype: tuple
1399 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1400 95ab4de9 David Knowles

1401 95ab4de9 David Knowles
    """
1402 95ab4de9 David Knowles
    query = []
1403 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1404 95ab4de9 David Knowles
1405 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1406 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1407 a198b2d9 Michael Hanselmann
                             query, None)
1408 95ab4de9 David Knowles
1409 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1410 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1411 95ab4de9 David Knowles

1412 95ab4de9 David Knowles
    @type bulk: bool
1413 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1414 95ab4de9 David Knowles

1415 95ab4de9 David Knowles
    @rtype: list of dict or str
1416 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1417 95ab4de9 David Knowles
        else list of nodes in the cluster
1418 95ab4de9 David Knowles

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

1433 95ab4de9 David Knowles
    @type node: str
1434 95ab4de9 David Knowles
    @param node: node whose info to return
1435 95ab4de9 David Knowles

1436 95ab4de9 David Knowles
    @rtype: dict
1437 95ab4de9 David Knowles
    @return: info about the node
1438 95ab4de9 David Knowles

1439 95ab4de9 David Knowles
    """
1440 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1441 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1442 a198b2d9 Michael Hanselmann
                             None, None)
1443 95ab4de9 David Knowles
1444 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1445 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1446 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1447 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1448 95ab4de9 David Knowles

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

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

1471 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1472 941b9309 Iustin Pop
        specified
1473 95ab4de9 David Knowles

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

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

1527 98805538 Michael Hanselmann
    @rtype: string
1528 95ab4de9 David Knowles
    @return: job id
1529 95ab4de9 David Knowles

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

1561 95ab4de9 David Knowles
    @type node: str
1562 95ab4de9 David Knowles
    @param node: node whose role to return
1563 95ab4de9 David Knowles

1564 95ab4de9 David Knowles
    @rtype: str
1565 95ab4de9 David Knowles
    @return: the current role for a node
1566 95ab4de9 David Knowles

1567 95ab4de9 David Knowles
    """
1568 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1569 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1570 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1571 95ab4de9 David Knowles
1572 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1573 95ab4de9 David Knowles
    """Sets the role for a node.
1574 95ab4de9 David Knowles

1575 95ab4de9 David Knowles
    @type node: str
1576 95ab4de9 David Knowles
    @param node: the node whose role to set
1577 95ab4de9 David Knowles
    @type role: str
1578 95ab4de9 David Knowles
    @param role: the role to set for the node
1579 95ab4de9 David Knowles
    @type force: bool
1580 95ab4de9 David Knowles
    @param force: whether to force the role change
1581 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1582 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1583 8de8e68d Michael Hanselmann
                         if necessary
1584 95ab4de9 David Knowles

1585 98805538 Michael Hanselmann
    @rtype: string
1586 95ab4de9 David Knowles
    @return: job id
1587 95ab4de9 David Knowles

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

1600 42d4d8b9 Michael Hanselmann
    @type node: string
1601 42d4d8b9 Michael Hanselmann
    @param node: Node name
1602 42d4d8b9 Michael Hanselmann
    @type force: bool
1603 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1604 42d4d8b9 Michael Hanselmann
    @rtype: string
1605 42d4d8b9 Michael Hanselmann
    @return: job id
1606 42d4d8b9 Michael Hanselmann

1607 42d4d8b9 Michael Hanselmann
    """
1608 4c864b55 Michael Hanselmann
    query = []
1609 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1610 42d4d8b9 Michael Hanselmann
1611 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1612 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1613 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1614 42d4d8b9 Michael Hanselmann
1615 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1616 94497dd1 Michael Hanselmann
    """Modifies a node.
1617 94497dd1 Michael Hanselmann

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

1620 370f2042 Guido Trotter
    @type node: string
1621 370f2042 Guido Trotter
    @param node: Node name
1622 94497dd1 Michael Hanselmann
    @rtype: string
1623 94497dd1 Michael Hanselmann
    @return: job id
1624 94497dd1 Michael Hanselmann

1625 94497dd1 Michael Hanselmann
    """
1626 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1627 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1628 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1629 94497dd1 Michael Hanselmann
1630 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1631 95ab4de9 David Knowles
    """Gets the storage units for a node.
1632 95ab4de9 David Knowles

1633 95ab4de9 David Knowles
    @type node: str
1634 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1635 95ab4de9 David Knowles
    @type storage_type: str
1636 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1637 95ab4de9 David Knowles
    @type output_fields: str
1638 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1639 95ab4de9 David Knowles

1640 98805538 Michael Hanselmann
    @rtype: string
1641 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1642 95ab4de9 David Knowles

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

1656 95ab4de9 David Knowles
    @type node: str
1657 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1658 95ab4de9 David Knowles
    @type storage_type: str
1659 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1660 95ab4de9 David Knowles
    @type name: str
1661 95ab4de9 David Knowles
    @param name: name of the storage unit
1662 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1663 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1664 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1665 95ab4de9 David Knowles

1666 98805538 Michael Hanselmann
    @rtype: string
1667 95ab4de9 David Knowles
    @return: job id
1668 95ab4de9 David Knowles

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

1684 95ab4de9 David Knowles
    @type node: str
1685 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1686 95ab4de9 David Knowles
    @type storage_type: str
1687 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1688 95ab4de9 David Knowles
    @type name: str
1689 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1690 95ab4de9 David Knowles

1691 98805538 Michael Hanselmann
    @rtype: string
1692 95ab4de9 David Knowles
    @return: job id
1693 95ab4de9 David Knowles

1694 95ab4de9 David Knowles
    """
1695 cfc03c54 Michael Hanselmann
    query = [
1696 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1697 cfc03c54 Michael Hanselmann
      ("name", name),
1698 cfc03c54 Michael Hanselmann
      ]
1699 95ab4de9 David Knowles
1700 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1701 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1702 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1703 95ab4de9 David Knowles
1704 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1705 95ab4de9 David Knowles
    """Gets the tags for a node.
1706 95ab4de9 David Knowles

1707 95ab4de9 David Knowles
    @type node: str
1708 95ab4de9 David Knowles
    @param node: node whose tags to return
1709 95ab4de9 David Knowles

1710 95ab4de9 David Knowles
    @rtype: list of str
1711 95ab4de9 David Knowles
    @return: tags for the node
1712 95ab4de9 David Knowles

1713 95ab4de9 David Knowles
    """
1714 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1715 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1716 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1717 95ab4de9 David Knowles
1718 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1719 95ab4de9 David Knowles
    """Adds tags to a node.
1720 95ab4de9 David Knowles

1721 95ab4de9 David Knowles
    @type node: str
1722 95ab4de9 David Knowles
    @param node: node to add tags to
1723 95ab4de9 David Knowles
    @type tags: list of str
1724 95ab4de9 David Knowles
    @param tags: tags to add to the node
1725 95ab4de9 David Knowles
    @type dry_run: bool
1726 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1727 95ab4de9 David Knowles

1728 98805538 Michael Hanselmann
    @rtype: string
1729 95ab4de9 David Knowles
    @return: job id
1730 95ab4de9 David Knowles

1731 95ab4de9 David Knowles
    """
1732 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1733 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1734 95ab4de9 David Knowles
1735 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1736 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1737 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1738 95ab4de9 David Knowles
1739 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1740 95ab4de9 David Knowles
    """Delete tags from a node.
1741 95ab4de9 David Knowles

1742 95ab4de9 David Knowles
    @type node: str
1743 95ab4de9 David Knowles
    @param node: node to remove tags from
1744 95ab4de9 David Knowles
    @type tags: list of str
1745 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1746 95ab4de9 David Knowles
    @type dry_run: bool
1747 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1748 95ab4de9 David Knowles

1749 98805538 Michael Hanselmann
    @rtype: string
1750 95ab4de9 David Knowles
    @return: job id
1751 95ab4de9 David Knowles

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

1763 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1764 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1765 4588b4bd Dimitris Aragiorgis

1766 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1767 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1768 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1769 4588b4bd Dimitris Aragiorgis

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

1784 1fa2c40b Dimitris Aragiorgis
    @type network: str
1785 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1786 4588b4bd Dimitris Aragiorgis

1787 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1788 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1789 4588b4bd Dimitris Aragiorgis

1790 4588b4bd Dimitris Aragiorgis
    """
1791 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1792 4588b4bd Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1793 4588b4bd Dimitris Aragiorgis
                             None, None)
1794 4588b4bd Dimitris Aragiorgis
1795 4588b4bd Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1796 5cfa6c37 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None,
1797 8140e24f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=None, dry_run=False):
1798 4588b4bd Dimitris Aragiorgis
    """Creates a new network.
1799 4588b4bd Dimitris Aragiorgis

1800 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1801 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1802 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1803 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1804 4588b4bd Dimitris Aragiorgis

1805 4588b4bd Dimitris Aragiorgis
    @rtype: string
1806 4588b4bd Dimitris Aragiorgis
    @return: job id
1807 4588b4bd Dimitris Aragiorgis

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

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

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

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

1869 6e8091f9 Dimitris Aragiorgis
    @type network: string
1870 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1871 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1872 6e8091f9 Dimitris Aragiorgis
    @return: job id
1873 4588b4bd Dimitris Aragiorgis

1874 6e8091f9 Dimitris Aragiorgis
    """
1875 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1876 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1877 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1878 4588b4bd Dimitris Aragiorgis
1879 4588b4bd Dimitris Aragiorgis
  def DeleteNetwork(self, network, dry_run=False):
1880 4588b4bd Dimitris Aragiorgis
    """Deletes a network.
1881 4588b4bd Dimitris Aragiorgis

1882 1fa2c40b Dimitris Aragiorgis
    @type network: str
1883 1fa2c40b Dimitris Aragiorgis
    @param network: the network to delete
1884 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1885 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1886 4588b4bd Dimitris Aragiorgis

1887 4588b4bd Dimitris Aragiorgis
    @rtype: string
1888 4588b4bd Dimitris Aragiorgis
    @return: job id
1889 4588b4bd Dimitris Aragiorgis

1890 4588b4bd Dimitris Aragiorgis
    """
1891 4588b4bd Dimitris Aragiorgis
    query = []
1892 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1893 4588b4bd Dimitris Aragiorgis
1894 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1895 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1896 4588b4bd Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1897 4588b4bd Dimitris Aragiorgis
1898 6e8091f9 Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1899 6e8091f9 Dimitris Aragiorgis
    """Gets tags for a network.
1900 6e8091f9 Dimitris Aragiorgis

1901 6e8091f9 Dimitris Aragiorgis
    @type network: string
1902 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1903 6e8091f9 Dimitris Aragiorgis

1904 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1905 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1906 6e8091f9 Dimitris Aragiorgis

1907 6e8091f9 Dimitris Aragiorgis
    """
1908 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1909 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1910 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1911 6e8091f9 Dimitris Aragiorgis
1912 6e8091f9 Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1913 6e8091f9 Dimitris Aragiorgis
    """Adds tags to a network.
1914 6e8091f9 Dimitris Aragiorgis

1915 6e8091f9 Dimitris Aragiorgis
    @type network: str
1916 6e8091f9 Dimitris Aragiorgis
    @param network: network to add tags to
1917 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1918 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to add to the network
1919 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1920 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1921 6e8091f9 Dimitris Aragiorgis

1922 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1923 6e8091f9 Dimitris Aragiorgis
    @return: job id
1924 6e8091f9 Dimitris Aragiorgis

1925 6e8091f9 Dimitris Aragiorgis
    """
1926 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1927 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1928 6e8091f9 Dimitris Aragiorgis
1929 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1930 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1931 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1932 6e8091f9 Dimitris Aragiorgis
1933 6e8091f9 Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1934 6e8091f9 Dimitris Aragiorgis
    """Deletes tags from a network.
1935 6e8091f9 Dimitris Aragiorgis

1936 6e8091f9 Dimitris Aragiorgis
    @type network: str
1937 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1938 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1939 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
1940 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1941 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1942 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1943 6e8091f9 Dimitris Aragiorgis
    @return: job id
1944 6e8091f9 Dimitris Aragiorgis

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

1956 a268af8d Adeodato Simo
    @type bulk: bool
1957 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1958 a268af8d Adeodato Simo

1959 a268af8d Adeodato Simo
    @rtype: list of dict or str
1960 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1961 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1962 a268af8d Adeodato Simo

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

1977 a268af8d Adeodato Simo
    @type group: str
1978 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1979 a268af8d Adeodato Simo

1980 a268af8d Adeodato Simo
    @rtype: dict
1981 a268af8d Adeodato Simo
    @return: info about the node group
1982 a268af8d Adeodato Simo

1983 a268af8d Adeodato Simo
    """
1984 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1985 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1986 a268af8d Adeodato Simo
                             None, None)
1987 a268af8d Adeodato Simo
1988 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1989 a268af8d Adeodato Simo
    """Creates a new node group.
1990 a268af8d Adeodato Simo

1991 a268af8d Adeodato Simo
    @type name: str
1992 a268af8d Adeodato Simo
    @param name: the name of node group to create
1993 90e99856 Adeodato Simo
    @type alloc_policy: str
1994 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
1995 a268af8d Adeodato Simo
    @type dry_run: bool
1996 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1997 a268af8d Adeodato Simo

1998 98805538 Michael Hanselmann
    @rtype: string
1999 a268af8d Adeodato Simo
    @return: job id
2000 a268af8d Adeodato Simo

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

2016 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
2017 f18fab7d Adeodato Simo

2018 f18fab7d Adeodato Simo
    @type group: string
2019 f18fab7d Adeodato Simo
    @param group: Node group name
2020 98805538 Michael Hanselmann
    @rtype: string
2021 f18fab7d Adeodato Simo
    @return: job id
2022 f18fab7d Adeodato Simo

2023 f18fab7d Adeodato Simo
    """
2024 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2025 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
2026 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
2027 f18fab7d Adeodato Simo
2028 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
2029 a268af8d Adeodato Simo
    """Deletes a node group.
2030 a268af8d Adeodato Simo

2031 a268af8d Adeodato Simo
    @type group: str
2032 a268af8d Adeodato Simo
    @param group: the node group to delete
2033 a268af8d Adeodato Simo
    @type dry_run: bool
2034 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2035 a268af8d Adeodato Simo

2036 98805538 Michael Hanselmann
    @rtype: string
2037 a268af8d Adeodato Simo
    @return: job id
2038 a268af8d Adeodato Simo

2039 a268af8d Adeodato Simo
    """
2040 a268af8d Adeodato Simo
    query = []
2041 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2042 a268af8d Adeodato Simo
2043 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
2044 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
2045 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
2046 a268af8d Adeodato Simo
2047 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
2048 a268af8d Adeodato Simo
    """Changes the name of a node group.
2049 a268af8d Adeodato Simo

2050 a268af8d Adeodato Simo
    @type group: string
2051 a268af8d Adeodato Simo
    @param group: Node group name
2052 a268af8d Adeodato Simo
    @type new_name: string
2053 a268af8d Adeodato Simo
    @param new_name: New node group name
2054 a268af8d Adeodato Simo

2055 98805538 Michael Hanselmann
    @rtype: string
2056 a268af8d Adeodato Simo
    @return: job id
2057 a268af8d Adeodato Simo

2058 a268af8d Adeodato Simo
    """
2059 a268af8d Adeodato Simo
    body = {
2060 a268af8d Adeodato Simo
      "new_name": new_name,
2061 a268af8d Adeodato Simo
      }
2062 a268af8d Adeodato Simo
2063 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2064 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
2065 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
2066 4245446f Adeodato Simo
2067 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
2068 4245446f Adeodato Simo
    """Assigns nodes to a group.
2069 4245446f Adeodato Simo

2070 4245446f Adeodato Simo
    @type group: string
2071 6ce90543 Michael Hanselmann
    @param group: Node group name
2072 4245446f Adeodato Simo
    @type nodes: list of strings
2073 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
2074 4245446f Adeodato Simo

2075 98805538 Michael Hanselmann
    @rtype: string
2076 4245446f Adeodato Simo
    @return: job id
2077 4245446f Adeodato Simo

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

2094 414ebaf1 Michael Hanselmann
    @type group: string
2095 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2096 414ebaf1 Michael Hanselmann

2097 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2098 414ebaf1 Michael Hanselmann
    @return: tags for the group
2099 414ebaf1 Michael Hanselmann

2100 414ebaf1 Michael Hanselmann
    """
2101 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2102 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2103 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
2104 414ebaf1 Michael Hanselmann
2105 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
2106 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
2107 414ebaf1 Michael Hanselmann

2108 414ebaf1 Michael Hanselmann
    @type group: str
2109 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
2110 414ebaf1 Michael Hanselmann
    @type tags: list of string
2111 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
2112 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2113 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2114 414ebaf1 Michael Hanselmann

2115 414ebaf1 Michael Hanselmann
    @rtype: string
2116 414ebaf1 Michael Hanselmann
    @return: job id
2117 414ebaf1 Michael Hanselmann

2118 414ebaf1 Michael Hanselmann
    """
2119 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2120 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2121 414ebaf1 Michael Hanselmann
2122 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2123 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2124 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2125 414ebaf1 Michael Hanselmann
2126 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
2127 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
2128 414ebaf1 Michael Hanselmann

2129 414ebaf1 Michael Hanselmann
    @type group: str
2130 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2131 414ebaf1 Michael Hanselmann
    @type tags: list of string
2132 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
2133 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2134 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2135 414ebaf1 Michael Hanselmann
    @rtype: string
2136 414ebaf1 Michael Hanselmann
    @return: job id
2137 414ebaf1 Michael Hanselmann

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

2149 208a6cff Michael Hanselmann
    @type what: string
2150 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2151 208a6cff Michael Hanselmann
    @type fields: list of string
2152 208a6cff Michael Hanselmann
    @param fields: Requested fields
2153 2e5c33db Iustin Pop
    @type qfilter: None or list
2154 2e5c33db Iustin Pop
    @param qfilter: Query filter
2155 208a6cff Michael Hanselmann

2156 208a6cff Michael Hanselmann
    @rtype: string
2157 208a6cff Michael Hanselmann
    @return: job id
2158 208a6cff Michael Hanselmann

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

2175 208a6cff Michael Hanselmann
    @type what: string
2176 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2177 208a6cff Michael Hanselmann
    @type fields: list of string
2178 208a6cff Michael Hanselmann
    @param fields: Requested fields
2179 208a6cff Michael Hanselmann

2180 208a6cff Michael Hanselmann
    @rtype: string
2181 208a6cff Michael Hanselmann
    @return: job id
2182 208a6cff Michael Hanselmann

2183 208a6cff Michael Hanselmann
    """
2184 208a6cff Michael Hanselmann
    query = []
2185 208a6cff Michael Hanselmann
2186 208a6cff Michael Hanselmann
    if fields is not None:
2187 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
2188 208a6cff Michael Hanselmann
2189 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2190 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
2191 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)