Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 3e02cd3c

History | View | Annotate | Download (62.7 kB)

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

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

31 2a7c3583 Michael Hanselmann
"""
32 95ab4de9 David Knowles
33 5ef5cfea Michael Hanselmann
# No Ganeti-specific modules should be imported. The RAPI client is supposed to
34 5ef5cfea Michael Hanselmann
# be standalone.
35 5ef5cfea Michael Hanselmann
36 9279e986 Michael Hanselmann
import logging
37 95ab4de9 David Knowles
import simplejson
38 1a8337f2 Manuel Franceschini
import socket
39 95ab4de9 David Knowles
import urllib
40 2a7c3583 Michael Hanselmann
import threading
41 2a7c3583 Michael Hanselmann
import pycurl
42 16c13387 Theo Van Dinter
import time
43 2a7c3583 Michael Hanselmann
44 2a7c3583 Michael Hanselmann
try:
45 2a7c3583 Michael Hanselmann
  from cStringIO import StringIO
46 2a7c3583 Michael Hanselmann
except ImportError:
47 2a7c3583 Michael Hanselmann
  from StringIO import StringIO
48 95ab4de9 David Knowles
49 95ab4de9 David Knowles
50 9279e986 Michael Hanselmann
GANETI_RAPI_PORT = 5080
51 a198b2d9 Michael Hanselmann
GANETI_RAPI_VERSION = 2
52 9279e986 Michael Hanselmann
53 95ab4de9 David Knowles
HTTP_DELETE = "DELETE"
54 95ab4de9 David Knowles
HTTP_GET = "GET"
55 95ab4de9 David Knowles
HTTP_PUT = "PUT"
56 95ab4de9 David Knowles
HTTP_POST = "POST"
57 9279e986 Michael Hanselmann
HTTP_OK = 200
58 7eac4a4d Michael Hanselmann
HTTP_NOT_FOUND = 404
59 9279e986 Michael Hanselmann
HTTP_APP_JSON = "application/json"
60 9279e986 Michael Hanselmann
61 95ab4de9 David Knowles
REPLACE_DISK_PRI = "replace_on_primary"
62 95ab4de9 David Knowles
REPLACE_DISK_SECONDARY = "replace_on_secondary"
63 95ab4de9 David Knowles
REPLACE_DISK_CHG = "replace_new_secondary"
64 95ab4de9 David Knowles
REPLACE_DISK_AUTO = "replace_auto"
65 1068639f Michael Hanselmann
66 0b58db81 Michael Hanselmann
NODE_EVAC_PRI = "primary-only"
67 0b58db81 Michael Hanselmann
NODE_EVAC_SEC = "secondary-only"
68 0b58db81 Michael Hanselmann
NODE_EVAC_ALL = "all"
69 0b58db81 Michael Hanselmann
70 1068639f Michael Hanselmann
NODE_ROLE_DRAINED = "drained"
71 1068639f Michael Hanselmann
NODE_ROLE_MASTER_CANDIATE = "master-candidate"
72 1068639f Michael Hanselmann
NODE_ROLE_MASTER = "master"
73 1068639f Michael Hanselmann
NODE_ROLE_OFFLINE = "offline"
74 1068639f Michael Hanselmann
NODE_ROLE_REGULAR = "regular"
75 95ab4de9 David Knowles
76 63d5eb8a Michael Hanselmann
JOB_STATUS_QUEUED = "queued"
77 47099cd1 Michael Hanselmann
JOB_STATUS_WAITING = "waiting"
78 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELING = "canceling"
79 63d5eb8a Michael Hanselmann
JOB_STATUS_RUNNING = "running"
80 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELED = "canceled"
81 63d5eb8a Michael Hanselmann
JOB_STATUS_SUCCESS = "success"
82 63d5eb8a Michael Hanselmann
JOB_STATUS_ERROR = "error"
83 1eaddbc6 Michael Hanselmann
JOB_STATUS_PENDING = frozenset([
84 1eaddbc6 Michael Hanselmann
  JOB_STATUS_QUEUED,
85 1eaddbc6 Michael Hanselmann
  JOB_STATUS_WAITING,
86 1eaddbc6 Michael Hanselmann
  JOB_STATUS_CANCELING,
87 1eaddbc6 Michael Hanselmann
  ])
88 63d5eb8a Michael Hanselmann
JOB_STATUS_FINALIZED = frozenset([
89 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELED,
90 63d5eb8a Michael Hanselmann
  JOB_STATUS_SUCCESS,
91 63d5eb8a Michael Hanselmann
  JOB_STATUS_ERROR,
92 63d5eb8a Michael Hanselmann
  ])
93 63d5eb8a Michael Hanselmann
JOB_STATUS_ALL = frozenset([
94 63d5eb8a Michael Hanselmann
  JOB_STATUS_RUNNING,
95 1eaddbc6 Michael Hanselmann
  ]) | JOB_STATUS_PENDING | JOB_STATUS_FINALIZED
96 63d5eb8a Michael Hanselmann
97 47099cd1 Michael Hanselmann
# Legacy name
98 47099cd1 Michael Hanselmann
JOB_STATUS_WAITLOCK = JOB_STATUS_WAITING
99 47099cd1 Michael Hanselmann
100 8a47b447 Michael Hanselmann
# Internal constants
101 8a47b447 Michael Hanselmann
_REQ_DATA_VERSION_FIELD = "__version__"
102 4c864b55 Michael Hanselmann
_QPARAM_DRY_RUN = "dry-run"
103 4c864b55 Michael Hanselmann
_QPARAM_FORCE = "force"
104 8a47b447 Michael Hanselmann
105 6396dc04 Michael Hanselmann
# Feature strings
106 6396dc04 Michael Hanselmann
INST_CREATE_REQV1 = "instance-create-reqv1"
107 6396dc04 Michael Hanselmann
INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
108 6396dc04 Michael Hanselmann
NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
109 6396dc04 Michael Hanselmann
NODE_EVAC_RES1 = "node-evac-res1"
110 6396dc04 Michael Hanselmann
111 6396dc04 Michael Hanselmann
# Old feature constant names in case they're references by users of this module
112 6396dc04 Michael Hanselmann
_INST_CREATE_REQV1 = INST_CREATE_REQV1
113 6396dc04 Michael Hanselmann
_INST_REINSTALL_REQV1 = INST_REINSTALL_REQV1
114 6396dc04 Michael Hanselmann
_NODE_MIGRATE_REQV1 = NODE_MIGRATE_REQV1
115 6396dc04 Michael Hanselmann
_NODE_EVAC_RES1 = NODE_EVAC_RES1
116 6396dc04 Michael Hanselmann
117 bc826292 Michael Hanselmann
#: Resolver errors
118 bc826292 Michael Hanselmann
ECODE_RESOLVER = "resolver_error"
119 bc826292 Michael Hanselmann
120 bc826292 Michael Hanselmann
#: Not enough resources (iallocator failure, disk space, memory, etc.)
121 bc826292 Michael Hanselmann
ECODE_NORES = "insufficient_resources"
122 bc826292 Michael Hanselmann
123 bc826292 Michael Hanselmann
#: Temporarily out of resources; operation can be tried again
124 e2dd6ece Michael Hanselmann
ECODE_TEMP_NORES = "temp_insufficient_resources"
125 bc826292 Michael Hanselmann
126 bc826292 Michael Hanselmann
#: Wrong arguments (at syntax level)
127 bc826292 Michael Hanselmann
ECODE_INVAL = "wrong_input"
128 bc826292 Michael Hanselmann
129 bc826292 Michael Hanselmann
#: Wrong entity state
130 bc826292 Michael Hanselmann
ECODE_STATE = "wrong_state"
131 bc826292 Michael Hanselmann
132 bc826292 Michael Hanselmann
#: Entity not found
133 bc826292 Michael Hanselmann
ECODE_NOENT = "unknown_entity"
134 bc826292 Michael Hanselmann
135 bc826292 Michael Hanselmann
#: Entity already exists
136 bc826292 Michael Hanselmann
ECODE_EXISTS = "already_exists"
137 bc826292 Michael Hanselmann
138 bc826292 Michael Hanselmann
#: Resource not unique (e.g. MAC or IP duplication)
139 bc826292 Michael Hanselmann
ECODE_NOTUNIQUE = "resource_not_unique"
140 bc826292 Michael Hanselmann
141 bc826292 Michael Hanselmann
#: Internal cluster error
142 bc826292 Michael Hanselmann
ECODE_FAULT = "internal_error"
143 bc826292 Michael Hanselmann
144 bc826292 Michael Hanselmann
#: Environment error (e.g. node disk error)
145 bc826292 Michael Hanselmann
ECODE_ENVIRON = "environment_error"
146 bc826292 Michael Hanselmann
147 bc826292 Michael Hanselmann
#: List of all failure types
148 bc826292 Michael Hanselmann
ECODE_ALL = frozenset([
149 bc826292 Michael Hanselmann
  ECODE_RESOLVER,
150 bc826292 Michael Hanselmann
  ECODE_NORES,
151 bc826292 Michael Hanselmann
  ECODE_TEMP_NORES,
152 bc826292 Michael Hanselmann
  ECODE_INVAL,
153 bc826292 Michael Hanselmann
  ECODE_STATE,
154 bc826292 Michael Hanselmann
  ECODE_NOENT,
155 bc826292 Michael Hanselmann
  ECODE_EXISTS,
156 bc826292 Michael Hanselmann
  ECODE_NOTUNIQUE,
157 bc826292 Michael Hanselmann
  ECODE_FAULT,
158 bc826292 Michael Hanselmann
  ECODE_ENVIRON,
159 bc826292 Michael Hanselmann
  ])
160 bc826292 Michael Hanselmann
161 2a7c3583 Michael Hanselmann
# Older pycURL versions don't have all error constants
162 2a7c3583 Michael Hanselmann
try:
163 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
164 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
165 2a7c3583 Michael Hanselmann
except AttributeError:
166 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = 60
167 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = 77
168 2a7c3583 Michael Hanselmann
169 2a7c3583 Michael Hanselmann
_CURL_SSL_CERT_ERRORS = frozenset([
170 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT,
171 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE,
172 2a7c3583 Michael Hanselmann
  ])
173 2a7c3583 Michael Hanselmann
174 95ab4de9 David Knowles
175 95ab4de9 David Knowles
class Error(Exception):
176 95ab4de9 David Knowles
  """Base error class for this module.
177 95ab4de9 David Knowles

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

818 95ab4de9 David Knowles
    """
819 95ab4de9 David Knowles
    query = []
820 8a47b447 Michael Hanselmann
821 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, kwargs.get("dry_run"))
822 95ab4de9 David Knowles
823 8a47b447 Michael Hanselmann
    if _INST_CREATE_REQV1 in self.GetFeatures():
824 915faf72 René Nussbaumer
      body = self.InstanceAllocation(mode, name, disk_template, disks, nics,
825 915faf72 René Nussbaumer
                                     **kwargs)
826 915faf72 René Nussbaumer
      body[_REQ_DATA_VERSION_FIELD] = 1
827 8a47b447 Michael Hanselmann
    else:
828 9a8ae794 Michael Hanselmann
      raise GanetiApiError("Server does not support new-style (version 1)"
829 9a8ae794 Michael Hanselmann
                           " instance creation requests")
830 8a47b447 Michael Hanselmann
831 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
832 8a47b447 Michael Hanselmann
                             query, body)
833 95ab4de9 David Knowles
834 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 95ab4de9 David Knowles
                     dry_run=False):
1005 95ab4de9 David Knowles
    """Reboots an instance.
1006 95ab4de9 David Knowles

1007 95ab4de9 David Knowles
    @type instance: str
1008 95ab4de9 David Knowles
    @param instance: instance to rebot
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 d914c76f Simeon Miteff
    @rtype: string
1017 d914c76f Simeon Miteff
    @return: job id
1018 95ab4de9 David Knowles

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

1034 95ab4de9 David Knowles
    @type instance: str
1035 95ab4de9 David Knowles
    @param instance: the instance to shut down
1036 95ab4de9 David Knowles
    @type dry_run: bool
1037 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1038 2ba39b8f Iustin Pop
    @type no_remember: bool
1039 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
1040 d914c76f Simeon Miteff
    @rtype: string
1041 d914c76f Simeon Miteff
    @return: job id
1042 95ab4de9 David Knowles

1043 95ab4de9 David Knowles
    """
1044 95ab4de9 David Knowles
    query = []
1045 b8481ebf Guido Trotter
    body = kwargs
1046 b8481ebf Guido Trotter
1047 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1048 7db266bb Daniel Krambrock
    _AppendIf(query, no_remember, ("no_remember", 1))
1049 95ab4de9 David Knowles
1050 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1051 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/shutdown" %
1052 b8481ebf Guido Trotter
                              (GANETI_RAPI_VERSION, instance)), query, body)
1053 95ab4de9 David Knowles
1054 2ba39b8f Iustin Pop
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
1055 95ab4de9 David Knowles
    """Starts up an instance.
1056 95ab4de9 David Knowles

1057 95ab4de9 David Knowles
    @type instance: str
1058 95ab4de9 David Knowles
    @param instance: the instance to start up
1059 95ab4de9 David Knowles
    @type dry_run: bool
1060 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1061 2ba39b8f Iustin Pop
    @type no_remember: bool
1062 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
1063 d914c76f Simeon Miteff
    @rtype: string
1064 d914c76f Simeon Miteff
    @return: job id
1065 95ab4de9 David Knowles

1066 95ab4de9 David Knowles
    """
1067 95ab4de9 David Knowles
    query = []
1068 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1069 7db266bb Daniel Krambrock
    _AppendIf(query, no_remember, ("no_remember", 1))
1070 95ab4de9 David Knowles
1071 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1072 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/startup" %
1073 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1074 95ab4de9 David Knowles
1075 c744425f Michael Hanselmann
  def ReinstallInstance(self, instance, os=None, no_startup=False,
1076 c744425f Michael Hanselmann
                        osparams=None):
1077 95ab4de9 David Knowles
    """Reinstalls an instance.
1078 95ab4de9 David Knowles

1079 95ab4de9 David Knowles
    @type instance: str
1080 fcee9675 David Knowles
    @param instance: The instance to reinstall
1081 fcee9675 David Knowles
    @type os: str or None
1082 fcee9675 David Knowles
    @param os: The operating system to reinstall. If None, the instance's
1083 fcee9675 David Knowles
        current operating system will be installed again
1084 95ab4de9 David Knowles
    @type no_startup: bool
1085 fcee9675 David Knowles
    @param no_startup: Whether to start the instance automatically
1086 d914c76f Simeon Miteff
    @rtype: string
1087 d914c76f Simeon Miteff
    @return: job id
1088 95ab4de9 David Knowles

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

1117 95ab4de9 David Knowles
    @type instance: str
1118 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1119 bfc2002f Michael Hanselmann
    @type disks: list of ints
1120 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1121 95ab4de9 David Knowles
    @type mode: str
1122 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1123 95ab4de9 David Knowles
    @type remote_node: str or None
1124 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1125 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1126 95ab4de9 David Knowles
    @type iallocator: str or None
1127 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1128 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1129 95ab4de9 David Knowles

1130 98805538 Michael Hanselmann
    @rtype: string
1131 95ab4de9 David Knowles
    @return: job id
1132 95ab4de9 David Knowles

1133 95ab4de9 David Knowles
    """
1134 cfc03c54 Michael Hanselmann
    query = [
1135 cfc03c54 Michael Hanselmann
      ("mode", mode),
1136 cfc03c54 Michael Hanselmann
      ]
1137 95ab4de9 David Knowles
1138 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1139 539d65ba Michael Hanselmann
1140 539d65ba Michael Hanselmann
    if disks is not None:
1141 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1142 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1143 bfc2002f Michael Hanselmann
1144 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1145 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1146 bfc2002f Michael Hanselmann
1147 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1148 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1149 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1150 95ab4de9 David Knowles
1151 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1152 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1153 ebeb600f Michael Hanselmann

1154 ebeb600f Michael Hanselmann
    @type instance: string
1155 ebeb600f Michael Hanselmann
    @param instance: Instance name
1156 ebeb600f Michael Hanselmann
    @type mode: string
1157 ebeb600f Michael Hanselmann
    @param mode: Export mode
1158 ebeb600f Michael Hanselmann
    @rtype: string
1159 ebeb600f Michael Hanselmann
    @return: Job ID
1160 ebeb600f Michael Hanselmann

1161 ebeb600f Michael Hanselmann
    """
1162 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1163 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1164 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1165 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1166 ebeb600f Michael Hanselmann
1167 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1168 ebeb600f Michael Hanselmann
                     remove_instance=None,
1169 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1170 ebeb600f Michael Hanselmann
    """Exports an instance.
1171 ebeb600f Michael Hanselmann

1172 ebeb600f Michael Hanselmann
    @type instance: string
1173 ebeb600f Michael Hanselmann
    @param instance: Instance name
1174 ebeb600f Michael Hanselmann
    @type mode: string
1175 ebeb600f Michael Hanselmann
    @param mode: Export mode
1176 ebeb600f Michael Hanselmann
    @rtype: string
1177 ebeb600f Michael Hanselmann
    @return: Job ID
1178 ebeb600f Michael Hanselmann

1179 ebeb600f Michael Hanselmann
    """
1180 ebeb600f Michael Hanselmann
    body = {
1181 ebeb600f Michael Hanselmann
      "destination": destination,
1182 ebeb600f Michael Hanselmann
      "mode": mode,
1183 ebeb600f Michael Hanselmann
      }
1184 ebeb600f Michael Hanselmann
1185 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1186 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1187 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1188 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1189 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1190 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1191 ebeb600f Michael Hanselmann
1192 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1193 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1194 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1195 ebeb600f Michael Hanselmann
1196 dc700f75 Daniel Krambrock
  def MigrateInstance(self, instance, mode=None, cleanup=None,
1197 dc700f75 Daniel Krambrock
                      target_node=None):
1198 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1199 e0ac6ce6 Michael Hanselmann

1200 e0ac6ce6 Michael Hanselmann
    @type instance: string
1201 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1202 e0ac6ce6 Michael Hanselmann
    @type mode: string
1203 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1204 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1205 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1206 dc700f75 Daniel Krambrock
    @type target_node: string
1207 dc700f75 Daniel Krambrock
    @param target_node: Target Node for externally mirrored instances
1208 d914c76f Simeon Miteff
    @rtype: string
1209 d914c76f Simeon Miteff
    @return: job id
1210 e0ac6ce6 Michael Hanselmann

1211 e0ac6ce6 Michael Hanselmann
    """
1212 e0ac6ce6 Michael Hanselmann
    body = {}
1213 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1214 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1215 dc700f75 Daniel Krambrock
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1216 e0ac6ce6 Michael Hanselmann
1217 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1218 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1219 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1220 e0ac6ce6 Michael Hanselmann
1221 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1222 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1223 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1224 c0a146a1 Michael Hanselmann

1225 c0a146a1 Michael Hanselmann
    @type instance: string
1226 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1227 c0a146a1 Michael Hanselmann
    @type iallocator: string
1228 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1229 c0a146a1 Michael Hanselmann
      shared-storage instances
1230 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1231 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1232 c0a146a1 Michael Hanselmann
    @type target_node: string
1233 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1234 c0a146a1 Michael Hanselmann
    @rtype: string
1235 c0a146a1 Michael Hanselmann
    @return: job id
1236 c0a146a1 Michael Hanselmann

1237 c0a146a1 Michael Hanselmann
    """
1238 c0a146a1 Michael Hanselmann
    body = {}
1239 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1240 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1241 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1242 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1243 c0a146a1 Michael Hanselmann
1244 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1245 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1246 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1247 c0a146a1 Michael Hanselmann
1248 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1249 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1250 d654aae1 Michael Hanselmann

1251 d654aae1 Michael Hanselmann
    @type instance: string
1252 d654aae1 Michael Hanselmann
    @param instance: Instance name
1253 d654aae1 Michael Hanselmann
    @type new_name: string
1254 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1255 d654aae1 Michael Hanselmann
    @type ip_check: bool
1256 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1257 d654aae1 Michael Hanselmann
    @type name_check: bool
1258 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1259 d914c76f Simeon Miteff
    @rtype: string
1260 d914c76f Simeon Miteff
    @return: job id
1261 d654aae1 Michael Hanselmann

1262 d654aae1 Michael Hanselmann
    """
1263 d654aae1 Michael Hanselmann
    body = {
1264 d654aae1 Michael Hanselmann
      "new_name": new_name,
1265 d654aae1 Michael Hanselmann
      }
1266 d654aae1 Michael Hanselmann
1267 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1268 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1269 d654aae1 Michael Hanselmann
1270 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1271 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1272 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1273 d654aae1 Michael Hanselmann
1274 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1275 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1276 b82d4c5e Michael Hanselmann

1277 b82d4c5e Michael Hanselmann
    @type instance: string
1278 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1279 d914c76f Simeon Miteff
    @rtype: dict
1280 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1281 b82d4c5e Michael Hanselmann

1282 b82d4c5e Michael Hanselmann
    """
1283 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1284 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1285 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1286 b82d4c5e Michael Hanselmann
1287 95ab4de9 David Knowles
  def GetJobs(self):
1288 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1289 95ab4de9 David Knowles

1290 95ab4de9 David Knowles
    @rtype: list of int
1291 95ab4de9 David Knowles
    @return: job ids for the cluster
1292 95ab4de9 David Knowles

1293 95ab4de9 David Knowles
    """
1294 768747ed Michael Hanselmann
    return [int(j["id"])
1295 a198b2d9 Michael Hanselmann
            for j in self._SendRequest(HTTP_GET,
1296 a198b2d9 Michael Hanselmann
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1297 a198b2d9 Michael Hanselmann
                                       None, None)]
1298 95ab4de9 David Knowles
1299 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1300 95ab4de9 David Knowles
    """Gets the status of a job.
1301 95ab4de9 David Knowles

1302 98805538 Michael Hanselmann
    @type job_id: string
1303 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1304 95ab4de9 David Knowles

1305 95ab4de9 David Knowles
    @rtype: dict
1306 95ab4de9 David Knowles
    @return: job status
1307 95ab4de9 David Knowles

1308 95ab4de9 David Knowles
    """
1309 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1310 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1311 a198b2d9 Michael Hanselmann
                             None, None)
1312 95ab4de9 David Knowles
1313 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1314 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1315 16c13387 Theo Van Dinter

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

1319 cfda0e48 Iustin Pop
    @type job_id: string
1320 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1321 16c13387 Theo Van Dinter
    @type period: int
1322 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1323 16c13387 Theo Van Dinter
    @type retries: int
1324 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1325 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1326 16c13387 Theo Van Dinter

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

1333 16c13387 Theo Van Dinter
    """
1334 16c13387 Theo Van Dinter
    while retries != 0:
1335 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1336 dde0e97c Michael Hanselmann
1337 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1338 16c13387 Theo Van Dinter
        return True
1339 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1340 dde0e97c Michael Hanselmann
        return False
1341 dde0e97c Michael Hanselmann
1342 dde0e97c Michael Hanselmann
      if period:
1343 dde0e97c Michael Hanselmann
        time.sleep(period)
1344 dde0e97c Michael Hanselmann
1345 16c13387 Theo Van Dinter
      if retries > 0:
1346 16c13387 Theo Van Dinter
        retries -= 1
1347 dde0e97c Michael Hanselmann
1348 16c13387 Theo Van Dinter
    return False
1349 16c13387 Theo Van Dinter
1350 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1351 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1352 d9b67f70 Michael Hanselmann

1353 98805538 Michael Hanselmann
    @type job_id: string
1354 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1355 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1356 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1357 d914c76f Simeon Miteff
    @rtype: dict
1358 d9b67f70 Michael Hanselmann

1359 d9b67f70 Michael Hanselmann
    """
1360 d9b67f70 Michael Hanselmann
    body = {
1361 d9b67f70 Michael Hanselmann
      "fields": fields,
1362 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1363 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1364 d9b67f70 Michael Hanselmann
      }
1365 d9b67f70 Michael Hanselmann
1366 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1367 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1368 a198b2d9 Michael Hanselmann
                             None, body)
1369 d9b67f70 Michael Hanselmann
1370 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1371 cf9ada49 Michael Hanselmann
    """Cancels a job.
1372 95ab4de9 David Knowles

1373 98805538 Michael Hanselmann
    @type job_id: string
1374 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1375 95ab4de9 David Knowles
    @type dry_run: bool
1376 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1377 d914c76f Simeon Miteff
    @rtype: tuple
1378 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1379 95ab4de9 David Knowles

1380 95ab4de9 David Knowles
    """
1381 95ab4de9 David Knowles
    query = []
1382 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1383 95ab4de9 David Knowles
1384 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1385 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1386 a198b2d9 Michael Hanselmann
                             query, None)
1387 95ab4de9 David Knowles
1388 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1389 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1390 95ab4de9 David Knowles

1391 95ab4de9 David Knowles
    @type bulk: bool
1392 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1393 95ab4de9 David Knowles

1394 95ab4de9 David Knowles
    @rtype: list of dict or str
1395 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1396 95ab4de9 David Knowles
        else list of nodes in the cluster
1397 95ab4de9 David Knowles

1398 95ab4de9 David Knowles
    """
1399 95ab4de9 David Knowles
    query = []
1400 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1401 95ab4de9 David Knowles
1402 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1403 a198b2d9 Michael Hanselmann
                              query, None)
1404 95ab4de9 David Knowles
    if bulk:
1405 95ab4de9 David Knowles
      return nodes
1406 95ab4de9 David Knowles
    else:
1407 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1408 95ab4de9 David Knowles
1409 591e5103 Michael Hanselmann
  def GetNode(self, node):
1410 95ab4de9 David Knowles
    """Gets information about a node.
1411 95ab4de9 David Knowles

1412 95ab4de9 David Knowles
    @type node: str
1413 95ab4de9 David Knowles
    @param node: node whose info to return
1414 95ab4de9 David Knowles

1415 95ab4de9 David Knowles
    @rtype: dict
1416 95ab4de9 David Knowles
    @return: info about the node
1417 95ab4de9 David Knowles

1418 95ab4de9 David Knowles
    """
1419 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1420 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1421 a198b2d9 Michael Hanselmann
                             None, None)
1422 95ab4de9 David Knowles
1423 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1424 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1425 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1426 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1427 95ab4de9 David Knowles

1428 95ab4de9 David Knowles
    @type node: str
1429 95ab4de9 David Knowles
    @param node: node to evacuate
1430 95ab4de9 David Knowles
    @type iallocator: str or None
1431 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1432 95ab4de9 David Knowles
    @type remote_node: str
1433 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1434 95ab4de9 David Knowles
    @type dry_run: bool
1435 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1436 941b9309 Iustin Pop
    @type early_release: bool
1437 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1438 0b58db81 Michael Hanselmann
    @type mode: string
1439 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1440 de40437a Michael Hanselmann
    @type accept_old: bool
1441 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1442 de40437a Michael Hanselmann
        results
1443 de40437a Michael Hanselmann

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

1450 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1451 941b9309 Iustin Pop
        specified
1452 95ab4de9 David Knowles

1453 95ab4de9 David Knowles
    """
1454 95ab4de9 David Knowles
    if iallocator and remote_node:
1455 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1456 95ab4de9 David Knowles
1457 cfc03c54 Michael Hanselmann
    query = []
1458 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1459 de40437a Michael Hanselmann
1460 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1461 0b58db81 Michael Hanselmann
      # Server supports body parameters
1462 de40437a Michael Hanselmann
      body = {}
1463 de40437a Michael Hanselmann
1464 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1465 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1466 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1467 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1468 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1469 de40437a Michael Hanselmann
    else:
1470 de40437a Michael Hanselmann
      # Pre-2.5 request format
1471 de40437a Michael Hanselmann
      body = None
1472 de40437a Michael Hanselmann
1473 de40437a Michael Hanselmann
      if not accept_old:
1474 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1475 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1476 de40437a Michael Hanselmann
                             " accept_old)")
1477 de40437a Michael Hanselmann
1478 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1479 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1480 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1481 de40437a Michael Hanselmann
1482 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1483 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1484 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1485 95ab4de9 David Knowles
1486 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1487 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1488 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1489 95ab4de9 David Knowles
1490 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1491 b7a1c816 Michael Hanselmann
                  target_node=None):
1492 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1493 95ab4de9 David Knowles

1494 95ab4de9 David Knowles
    @type node: str
1495 95ab4de9 David Knowles
    @param node: node to migrate
1496 1f334d96 Iustin Pop
    @type mode: string
1497 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1498 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1499 95ab4de9 David Knowles
    @type dry_run: bool
1500 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1501 b7a1c816 Michael Hanselmann
    @type iallocator: string
1502 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1503 b7a1c816 Michael Hanselmann
    @type target_node: string
1504 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1505 95ab4de9 David Knowles

1506 98805538 Michael Hanselmann
    @rtype: string
1507 95ab4de9 David Knowles
    @return: job id
1508 95ab4de9 David Knowles

1509 95ab4de9 David Knowles
    """
1510 95ab4de9 David Knowles
    query = []
1511 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1512 95ab4de9 David Knowles
1513 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1514 b7a1c816 Michael Hanselmann
      body = {}
1515 b7a1c816 Michael Hanselmann
1516 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1517 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1518 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1519 b7a1c816 Michael Hanselmann
1520 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1521 b7a1c816 Michael Hanselmann
1522 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1523 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1524 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1525 b7a1c816 Michael Hanselmann
    else:
1526 b7a1c816 Michael Hanselmann
      # Use old request format
1527 b7a1c816 Michael Hanselmann
      if target_node is not None:
1528 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1529 b7a1c816 Michael Hanselmann
                             " for node migration")
1530 b7a1c816 Michael Hanselmann
1531 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1532 b7a1c816 Michael Hanselmann
1533 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1534 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1535 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1536 95ab4de9 David Knowles
1537 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1538 95ab4de9 David Knowles
    """Gets the current role for a node.
1539 95ab4de9 David Knowles

1540 95ab4de9 David Knowles
    @type node: str
1541 95ab4de9 David Knowles
    @param node: node whose role to return
1542 95ab4de9 David Knowles

1543 95ab4de9 David Knowles
    @rtype: str
1544 95ab4de9 David Knowles
    @return: the current role for a node
1545 95ab4de9 David Knowles

1546 95ab4de9 David Knowles
    """
1547 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1548 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1549 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1550 95ab4de9 David Knowles
1551 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1552 95ab4de9 David Knowles
    """Sets the role for a node.
1553 95ab4de9 David Knowles

1554 95ab4de9 David Knowles
    @type node: str
1555 95ab4de9 David Knowles
    @param node: the node whose role to set
1556 95ab4de9 David Knowles
    @type role: str
1557 95ab4de9 David Knowles
    @param role: the role to set for the node
1558 95ab4de9 David Knowles
    @type force: bool
1559 95ab4de9 David Knowles
    @param force: whether to force the role change
1560 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1561 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1562 8de8e68d Michael Hanselmann
                         if necessary
1563 95ab4de9 David Knowles

1564 98805538 Michael Hanselmann
    @rtype: string
1565 95ab4de9 David Knowles
    @return: job id
1566 95ab4de9 David Knowles

1567 95ab4de9 David Knowles
    """
1568 4c864b55 Michael Hanselmann
    query = []
1569 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1570 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1571 8de8e68d Michael Hanselmann
1572 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1573 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1574 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1575 95ab4de9 David Knowles
1576 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1577 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1578 42d4d8b9 Michael Hanselmann

1579 42d4d8b9 Michael Hanselmann
    @type node: string
1580 42d4d8b9 Michael Hanselmann
    @param node: Node name
1581 42d4d8b9 Michael Hanselmann
    @type force: bool
1582 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1583 42d4d8b9 Michael Hanselmann
    @rtype: string
1584 42d4d8b9 Michael Hanselmann
    @return: job id
1585 42d4d8b9 Michael Hanselmann

1586 42d4d8b9 Michael Hanselmann
    """
1587 4c864b55 Michael Hanselmann
    query = []
1588 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1589 42d4d8b9 Michael Hanselmann
1590 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1591 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1592 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1593 42d4d8b9 Michael Hanselmann
1594 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1595 94497dd1 Michael Hanselmann
    """Modifies a node.
1596 94497dd1 Michael Hanselmann

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

1599 370f2042 Guido Trotter
    @type node: string
1600 370f2042 Guido Trotter
    @param node: Node name
1601 94497dd1 Michael Hanselmann
    @rtype: string
1602 94497dd1 Michael Hanselmann
    @return: job id
1603 94497dd1 Michael Hanselmann

1604 94497dd1 Michael Hanselmann
    """
1605 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1606 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1607 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1608 94497dd1 Michael Hanselmann
1609 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1610 95ab4de9 David Knowles
    """Gets the storage units for a node.
1611 95ab4de9 David Knowles

1612 95ab4de9 David Knowles
    @type node: str
1613 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1614 95ab4de9 David Knowles
    @type storage_type: str
1615 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1616 95ab4de9 David Knowles
    @type output_fields: str
1617 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1618 95ab4de9 David Knowles

1619 98805538 Michael Hanselmann
    @rtype: string
1620 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1621 95ab4de9 David Knowles

1622 95ab4de9 David Knowles
    """
1623 cfc03c54 Michael Hanselmann
    query = [
1624 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1625 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1626 cfc03c54 Michael Hanselmann
      ]
1627 95ab4de9 David Knowles
1628 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1629 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1630 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1631 95ab4de9 David Knowles
1632 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1633 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1634 95ab4de9 David Knowles

1635 95ab4de9 David Knowles
    @type node: str
1636 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1637 95ab4de9 David Knowles
    @type storage_type: str
1638 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1639 95ab4de9 David Knowles
    @type name: str
1640 95ab4de9 David Knowles
    @param name: name of the storage unit
1641 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1642 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1643 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1644 95ab4de9 David Knowles

1645 98805538 Michael Hanselmann
    @rtype: string
1646 95ab4de9 David Knowles
    @return: job id
1647 95ab4de9 David Knowles

1648 95ab4de9 David Knowles
    """
1649 95ab4de9 David Knowles
    query = [
1650 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1651 cfc03c54 Michael Hanselmann
      ("name", name),
1652 cfc03c54 Michael Hanselmann
      ]
1653 cfc03c54 Michael Hanselmann
1654 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1655 fde28316 Michael Hanselmann
1656 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1657 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1658 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1659 95ab4de9 David Knowles
1660 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1661 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1662 95ab4de9 David Knowles

1663 95ab4de9 David Knowles
    @type node: str
1664 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1665 95ab4de9 David Knowles
    @type storage_type: str
1666 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1667 95ab4de9 David Knowles
    @type name: str
1668 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1669 95ab4de9 David Knowles

1670 98805538 Michael Hanselmann
    @rtype: string
1671 95ab4de9 David Knowles
    @return: job id
1672 95ab4de9 David Knowles

1673 95ab4de9 David Knowles
    """
1674 cfc03c54 Michael Hanselmann
    query = [
1675 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1676 cfc03c54 Michael Hanselmann
      ("name", name),
1677 cfc03c54 Michael Hanselmann
      ]
1678 95ab4de9 David Knowles
1679 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1680 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1681 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1682 95ab4de9 David Knowles
1683 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1684 95ab4de9 David Knowles
    """Gets the tags for a node.
1685 95ab4de9 David Knowles

1686 95ab4de9 David Knowles
    @type node: str
1687 95ab4de9 David Knowles
    @param node: node whose tags to return
1688 95ab4de9 David Knowles

1689 95ab4de9 David Knowles
    @rtype: list of str
1690 95ab4de9 David Knowles
    @return: tags for the node
1691 95ab4de9 David Knowles

1692 95ab4de9 David Knowles
    """
1693 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1694 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1695 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1696 95ab4de9 David Knowles
1697 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1698 95ab4de9 David Knowles
    """Adds tags to a node.
1699 95ab4de9 David Knowles

1700 95ab4de9 David Knowles
    @type node: str
1701 95ab4de9 David Knowles
    @param node: node to add tags to
1702 95ab4de9 David Knowles
    @type tags: list of str
1703 95ab4de9 David Knowles
    @param tags: tags to add to the node
1704 95ab4de9 David Knowles
    @type dry_run: bool
1705 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1706 95ab4de9 David Knowles

1707 98805538 Michael Hanselmann
    @rtype: string
1708 95ab4de9 David Knowles
    @return: job id
1709 95ab4de9 David Knowles

1710 95ab4de9 David Knowles
    """
1711 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1712 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1713 95ab4de9 David Knowles
1714 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1715 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1716 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1717 95ab4de9 David Knowles
1718 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1719 95ab4de9 David Knowles
    """Delete tags from a node.
1720 95ab4de9 David Knowles

1721 95ab4de9 David Knowles
    @type node: str
1722 95ab4de9 David Knowles
    @param node: node to remove tags from
1723 95ab4de9 David Knowles
    @type tags: list of str
1724 95ab4de9 David Knowles
    @param tags: tags to remove from 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_DELETE,
1736 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1737 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1738 a268af8d Adeodato Simo
1739 4588b4bd Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1740 4588b4bd Dimitris Aragiorgis
    """Gets all networks in the cluster.
1741 4588b4bd Dimitris Aragiorgis

1742 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1743 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1744 4588b4bd Dimitris Aragiorgis

1745 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1746 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1747 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1748 4588b4bd Dimitris Aragiorgis

1749 4588b4bd Dimitris Aragiorgis
    """
1750 4588b4bd Dimitris Aragiorgis
    query = []
1751 4588b4bd Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1752 4588b4bd Dimitris Aragiorgis
1753 4588b4bd Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1754 3c286190 Dimitris Aragiorgis
                                 query, None)
1755 4588b4bd Dimitris Aragiorgis
    if bulk:
1756 4588b4bd Dimitris Aragiorgis
      return networks
1757 4588b4bd Dimitris Aragiorgis
    else:
1758 4588b4bd Dimitris Aragiorgis
      return [n["name"] for n in networks]
1759 4588b4bd Dimitris Aragiorgis
1760 4588b4bd Dimitris Aragiorgis
  def GetNetwork(self, network):
1761 4588b4bd Dimitris Aragiorgis
    """Gets information about a network.
1762 4588b4bd Dimitris Aragiorgis

1763 1fa2c40b Dimitris Aragiorgis
    @type network: str
1764 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1765 4588b4bd Dimitris Aragiorgis

1766 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1767 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1768 4588b4bd Dimitris Aragiorgis

1769 4588b4bd Dimitris Aragiorgis
    """
1770 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1771 4588b4bd Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1772 4588b4bd Dimitris Aragiorgis
                             None, None)
1773 4588b4bd Dimitris Aragiorgis
1774 4588b4bd Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1775 5cfa6c37 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None,
1776 8140e24f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=None, dry_run=False):
1777 4588b4bd Dimitris Aragiorgis
    """Creates a new network.
1778 4588b4bd Dimitris Aragiorgis

1779 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1780 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1781 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1782 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1783 4588b4bd Dimitris Aragiorgis

1784 4588b4bd Dimitris Aragiorgis
    @rtype: string
1785 4588b4bd Dimitris Aragiorgis
    @return: job id
1786 4588b4bd Dimitris Aragiorgis

1787 4588b4bd Dimitris Aragiorgis
    """
1788 4588b4bd Dimitris Aragiorgis
    query = []
1789 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1790 4588b4bd Dimitris Aragiorgis
1791 8140e24f Dimitris Aragiorgis
    if add_reserved_ips:
1792 2556424d Iustin Pop
      add_reserved_ips = add_reserved_ips.split(",")
1793 8140e24f Dimitris Aragiorgis
1794 8140e24f Dimitris Aragiorgis
    if tags:
1795 2556424d Iustin Pop
      tags = tags.split(",")
1796 8140e24f Dimitris Aragiorgis
1797 4588b4bd Dimitris Aragiorgis
    body = {
1798 4588b4bd Dimitris Aragiorgis
      "network_name": network_name,
1799 4588b4bd Dimitris Aragiorgis
      "gateway": gateway,
1800 4588b4bd Dimitris Aragiorgis
      "network": network,
1801 4588b4bd Dimitris Aragiorgis
      "gateway6": gateway6,
1802 4588b4bd Dimitris Aragiorgis
      "network6": network6,
1803 4588b4bd Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1804 8140e24f Dimitris Aragiorgis
      "add_reserved_ips": add_reserved_ips,
1805 8140e24f Dimitris Aragiorgis
      "tags": tags,
1806 4588b4bd Dimitris Aragiorgis
      }
1807 4588b4bd Dimitris Aragiorgis
1808 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1809 4588b4bd Dimitris Aragiorgis
                             query, body)
1810 4588b4bd Dimitris Aragiorgis
1811 6e8091f9 Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link, dry_run=False):
1812 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1813 4588b4bd Dimitris Aragiorgis

1814 4588b4bd Dimitris Aragiorgis
    """
1815 4588b4bd Dimitris Aragiorgis
    body = {
1816 4588b4bd Dimitris Aragiorgis
      "group_name": group_name,
1817 4588b4bd Dimitris Aragiorgis
      "network_mode": mode,
1818 3c286190 Dimitris Aragiorgis
      "network_link": link,
1819 4588b4bd Dimitris Aragiorgis
      }
1820 4588b4bd Dimitris Aragiorgis
1821 6e8091f9 Dimitris Aragiorgis
    query = []
1822 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1823 6e8091f9 Dimitris Aragiorgis
1824 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1825 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1826 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1827 4588b4bd Dimitris Aragiorgis
1828 6e8091f9 Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name, dry_run=False):
1829 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1830 4588b4bd Dimitris Aragiorgis

1831 4588b4bd Dimitris Aragiorgis
    """
1832 4588b4bd Dimitris Aragiorgis
    body = {
1833 3c286190 Dimitris Aragiorgis
      "group_name": group_name,
1834 4588b4bd Dimitris Aragiorgis
      }
1835 6e8091f9 Dimitris Aragiorgis
1836 6e8091f9 Dimitris Aragiorgis
    query = []
1837 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1838 6e8091f9 Dimitris Aragiorgis
1839 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1840 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1841 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1842 6e8091f9 Dimitris Aragiorgis
1843 6e8091f9 Dimitris Aragiorgis
  def ModifyNetwork(self, network, **kwargs):
1844 6e8091f9 Dimitris Aragiorgis
    """Modifies a network.
1845 6e8091f9 Dimitris Aragiorgis

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

1848 6e8091f9 Dimitris Aragiorgis
    @type network: string
1849 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1850 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1851 6e8091f9 Dimitris Aragiorgis
    @return: job id
1852 4588b4bd Dimitris Aragiorgis

1853 6e8091f9 Dimitris Aragiorgis
    """
1854 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1855 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1856 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1857 4588b4bd Dimitris Aragiorgis
1858 4588b4bd Dimitris Aragiorgis
  def DeleteNetwork(self, network, dry_run=False):
1859 4588b4bd Dimitris Aragiorgis
    """Deletes a network.
1860 4588b4bd Dimitris Aragiorgis

1861 1fa2c40b Dimitris Aragiorgis
    @type network: str
1862 1fa2c40b Dimitris Aragiorgis
    @param network: the network to delete
1863 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1864 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1865 4588b4bd Dimitris Aragiorgis

1866 4588b4bd Dimitris Aragiorgis
    @rtype: string
1867 4588b4bd Dimitris Aragiorgis
    @return: job id
1868 4588b4bd Dimitris Aragiorgis

1869 4588b4bd Dimitris Aragiorgis
    """
1870 4588b4bd Dimitris Aragiorgis
    query = []
1871 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1872 4588b4bd Dimitris Aragiorgis
1873 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1874 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1875 4588b4bd Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1876 4588b4bd Dimitris Aragiorgis
1877 6e8091f9 Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1878 6e8091f9 Dimitris Aragiorgis
    """Gets tags for a network.
1879 6e8091f9 Dimitris Aragiorgis

1880 6e8091f9 Dimitris Aragiorgis
    @type network: string
1881 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1882 6e8091f9 Dimitris Aragiorgis

1883 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1884 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1885 6e8091f9 Dimitris Aragiorgis

1886 6e8091f9 Dimitris Aragiorgis
    """
1887 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1888 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1889 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1890 6e8091f9 Dimitris Aragiorgis
1891 6e8091f9 Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1892 6e8091f9 Dimitris Aragiorgis
    """Adds tags to a network.
1893 6e8091f9 Dimitris Aragiorgis

1894 6e8091f9 Dimitris Aragiorgis
    @type network: str
1895 6e8091f9 Dimitris Aragiorgis
    @param network: network to add tags to
1896 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1897 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to add to the network
1898 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1899 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1900 6e8091f9 Dimitris Aragiorgis

1901 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1902 6e8091f9 Dimitris Aragiorgis
    @return: job id
1903 6e8091f9 Dimitris Aragiorgis

1904 6e8091f9 Dimitris Aragiorgis
    """
1905 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1906 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1907 6e8091f9 Dimitris Aragiorgis
1908 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1909 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1910 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1911 6e8091f9 Dimitris Aragiorgis
1912 6e8091f9 Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1913 6e8091f9 Dimitris Aragiorgis
    """Deletes tags from a network.
1914 6e8091f9 Dimitris Aragiorgis

1915 6e8091f9 Dimitris Aragiorgis
    @type network: str
1916 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1917 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1918 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
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
    @rtype: string
1922 6e8091f9 Dimitris Aragiorgis
    @return: job id
1923 6e8091f9 Dimitris Aragiorgis

1924 6e8091f9 Dimitris Aragiorgis
    """
1925 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1926 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1927 6e8091f9 Dimitris Aragiorgis
1928 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1929 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1930 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1931 6e8091f9 Dimitris Aragiorgis
1932 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1933 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1934 a268af8d Adeodato Simo

1935 a268af8d Adeodato Simo
    @type bulk: bool
1936 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1937 a268af8d Adeodato Simo

1938 a268af8d Adeodato Simo
    @rtype: list of dict or str
1939 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1940 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1941 a268af8d Adeodato Simo

1942 a268af8d Adeodato Simo
    """
1943 a268af8d Adeodato Simo
    query = []
1944 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1945 a268af8d Adeodato Simo
1946 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1947 a268af8d Adeodato Simo
                               query, None)
1948 a268af8d Adeodato Simo
    if bulk:
1949 a268af8d Adeodato Simo
      return groups
1950 a268af8d Adeodato Simo
    else:
1951 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1952 a268af8d Adeodato Simo
1953 a268af8d Adeodato Simo
  def GetGroup(self, group):
1954 a268af8d Adeodato Simo
    """Gets information about a node group.
1955 a268af8d Adeodato Simo

1956 a268af8d Adeodato Simo
    @type group: str
1957 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1958 a268af8d Adeodato Simo

1959 a268af8d Adeodato Simo
    @rtype: dict
1960 a268af8d Adeodato Simo
    @return: info about the node group
1961 a268af8d Adeodato Simo

1962 a268af8d Adeodato Simo
    """
1963 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1964 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1965 a268af8d Adeodato Simo
                             None, None)
1966 a268af8d Adeodato Simo
1967 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1968 a268af8d Adeodato Simo
    """Creates a new node group.
1969 a268af8d Adeodato Simo

1970 a268af8d Adeodato Simo
    @type name: str
1971 a268af8d Adeodato Simo
    @param name: the name of node group to create
1972 90e99856 Adeodato Simo
    @type alloc_policy: str
1973 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
1974 a268af8d Adeodato Simo
    @type dry_run: bool
1975 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1976 a268af8d Adeodato Simo

1977 98805538 Michael Hanselmann
    @rtype: string
1978 a268af8d Adeodato Simo
    @return: job id
1979 a268af8d Adeodato Simo

1980 a268af8d Adeodato Simo
    """
1981 a268af8d Adeodato Simo
    query = []
1982 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1983 a268af8d Adeodato Simo
1984 a268af8d Adeodato Simo
    body = {
1985 a268af8d Adeodato Simo
      "name": name,
1986 3c286190 Dimitris Aragiorgis
      "alloc_policy": alloc_policy,
1987 a268af8d Adeodato Simo
      }
1988 a268af8d Adeodato Simo
1989 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1990 a268af8d Adeodato Simo
                             query, body)
1991 a268af8d Adeodato Simo
1992 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
1993 f18fab7d Adeodato Simo
    """Modifies a node group.
1994 f18fab7d Adeodato Simo

1995 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
1996 f18fab7d Adeodato Simo

1997 f18fab7d Adeodato Simo
    @type group: string
1998 f18fab7d Adeodato Simo
    @param group: Node group name
1999 98805538 Michael Hanselmann
    @rtype: string
2000 f18fab7d Adeodato Simo
    @return: job id
2001 f18fab7d Adeodato Simo

2002 f18fab7d Adeodato Simo
    """
2003 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2004 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
2005 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
2006 f18fab7d Adeodato Simo
2007 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
2008 a268af8d Adeodato Simo
    """Deletes a node group.
2009 a268af8d Adeodato Simo

2010 a268af8d Adeodato Simo
    @type group: str
2011 a268af8d Adeodato Simo
    @param group: the node group to delete
2012 a268af8d Adeodato Simo
    @type dry_run: bool
2013 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2014 a268af8d Adeodato Simo

2015 98805538 Michael Hanselmann
    @rtype: string
2016 a268af8d Adeodato Simo
    @return: job id
2017 a268af8d Adeodato Simo

2018 a268af8d Adeodato Simo
    """
2019 a268af8d Adeodato Simo
    query = []
2020 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2021 a268af8d Adeodato Simo
2022 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
2023 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
2024 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
2025 a268af8d Adeodato Simo
2026 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
2027 a268af8d Adeodato Simo
    """Changes the name of a node group.
2028 a268af8d Adeodato Simo

2029 a268af8d Adeodato Simo
    @type group: string
2030 a268af8d Adeodato Simo
    @param group: Node group name
2031 a268af8d Adeodato Simo
    @type new_name: string
2032 a268af8d Adeodato Simo
    @param new_name: New node group name
2033 a268af8d Adeodato Simo

2034 98805538 Michael Hanselmann
    @rtype: string
2035 a268af8d Adeodato Simo
    @return: job id
2036 a268af8d Adeodato Simo

2037 a268af8d Adeodato Simo
    """
2038 a268af8d Adeodato Simo
    body = {
2039 a268af8d Adeodato Simo
      "new_name": new_name,
2040 a268af8d Adeodato Simo
      }
2041 a268af8d Adeodato Simo
2042 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2043 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
2044 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
2045 4245446f Adeodato Simo
2046 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
2047 4245446f Adeodato Simo
    """Assigns nodes to a group.
2048 4245446f Adeodato Simo

2049 4245446f Adeodato Simo
    @type group: string
2050 6ce90543 Michael Hanselmann
    @param group: Node group name
2051 4245446f Adeodato Simo
    @type nodes: list of strings
2052 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
2053 4245446f Adeodato Simo

2054 98805538 Michael Hanselmann
    @rtype: string
2055 4245446f Adeodato Simo
    @return: job id
2056 4245446f Adeodato Simo

2057 4245446f Adeodato Simo
    """
2058 4245446f Adeodato Simo
    query = []
2059 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
2060 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2061 4245446f Adeodato Simo
2062 4245446f Adeodato Simo
    body = {
2063 4245446f Adeodato Simo
      "nodes": nodes,
2064 4245446f Adeodato Simo
      }
2065 4245446f Adeodato Simo
2066 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2067 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
2068 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
2069 208a6cff Michael Hanselmann
2070 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
2071 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
2072 414ebaf1 Michael Hanselmann

2073 414ebaf1 Michael Hanselmann
    @type group: string
2074 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2075 414ebaf1 Michael Hanselmann

2076 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2077 414ebaf1 Michael Hanselmann
    @return: tags for the group
2078 414ebaf1 Michael Hanselmann

2079 414ebaf1 Michael Hanselmann
    """
2080 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2081 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2082 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
2083 414ebaf1 Michael Hanselmann
2084 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
2085 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
2086 414ebaf1 Michael Hanselmann

2087 414ebaf1 Michael Hanselmann
    @type group: str
2088 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
2089 414ebaf1 Michael Hanselmann
    @type tags: list of string
2090 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
2091 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2092 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2093 414ebaf1 Michael Hanselmann

2094 414ebaf1 Michael Hanselmann
    @rtype: string
2095 414ebaf1 Michael Hanselmann
    @return: job id
2096 414ebaf1 Michael Hanselmann

2097 414ebaf1 Michael Hanselmann
    """
2098 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2099 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2100 414ebaf1 Michael Hanselmann
2101 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2102 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2103 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2104 414ebaf1 Michael Hanselmann
2105 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
2106 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
2107 414ebaf1 Michael Hanselmann

2108 414ebaf1 Michael Hanselmann
    @type group: str
2109 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2110 414ebaf1 Michael Hanselmann
    @type tags: list of string
2111 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
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
    @rtype: string
2115 414ebaf1 Michael Hanselmann
    @return: job id
2116 414ebaf1 Michael Hanselmann

2117 414ebaf1 Michael Hanselmann
    """
2118 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2119 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2120 414ebaf1 Michael Hanselmann
2121 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
2122 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2123 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2124 414ebaf1 Michael Hanselmann
2125 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
2126 208a6cff Michael Hanselmann
    """Retrieves information about resources.
2127 208a6cff Michael Hanselmann

2128 208a6cff Michael Hanselmann
    @type what: string
2129 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2130 208a6cff Michael Hanselmann
    @type fields: list of string
2131 208a6cff Michael Hanselmann
    @param fields: Requested fields
2132 2e5c33db Iustin Pop
    @type qfilter: None or list
2133 2e5c33db Iustin Pop
    @param qfilter: Query filter
2134 208a6cff Michael Hanselmann

2135 208a6cff Michael Hanselmann
    @rtype: string
2136 208a6cff Michael Hanselmann
    @return: job id
2137 208a6cff Michael Hanselmann

2138 208a6cff Michael Hanselmann
    """
2139 208a6cff Michael Hanselmann
    body = {
2140 208a6cff Michael Hanselmann
      "fields": fields,
2141 208a6cff Michael Hanselmann
      }
2142 208a6cff Michael Hanselmann
2143 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
2144 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
2145 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
2146 208a6cff Michael Hanselmann
2147 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2148 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
2149 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
2150 208a6cff Michael Hanselmann
2151 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
2152 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
2153 208a6cff Michael Hanselmann

2154 208a6cff Michael Hanselmann
    @type what: string
2155 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2156 208a6cff Michael Hanselmann
    @type fields: list of string
2157 208a6cff Michael Hanselmann
    @param fields: Requested fields
2158 208a6cff Michael Hanselmann

2159 208a6cff Michael Hanselmann
    @rtype: string
2160 208a6cff Michael Hanselmann
    @return: job id
2161 208a6cff Michael Hanselmann

2162 208a6cff Michael Hanselmann
    """
2163 208a6cff Michael Hanselmann
    query = []
2164 208a6cff Michael Hanselmann
2165 208a6cff Michael Hanselmann
    if fields is not None:
2166 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
2167 208a6cff Michael Hanselmann
2168 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2169 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
2170 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)