Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (64.1 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1126 3a9fe2bc Dimitris Aragiorgis
    @type instance: str
1127 3a9fe2bc Dimitris Aragiorgis
    @param instance: instance whose disks to replace
1128 3a9fe2bc Dimitris Aragiorgis
    @type disks: list
1129 3a9fe2bc Dimitris Aragiorgis
    @param disks: list of tuples (idend, snapshot_name)
1130 3a9fe2bc Dimitris Aragiorgis

1131 3a9fe2bc Dimitris Aragiorgis
    @rtype: string
1132 3a9fe2bc Dimitris Aragiorgis
    @return: job id
1133 3a9fe2bc Dimitris Aragiorgis

1134 3a9fe2bc Dimitris Aragiorgis
    """
1135 3a9fe2bc Dimitris Aragiorgis
1136 3a9fe2bc Dimitris Aragiorgis
    body = {
1137 3a9fe2bc Dimitris Aragiorgis
      "disks": disks,
1138 3a9fe2bc Dimitris Aragiorgis
      }
1139 3a9fe2bc Dimitris Aragiorgis
1140 3a9fe2bc Dimitris Aragiorgis
    query = []
1141 3a9fe2bc Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1142 3a9fe2bc Dimitris Aragiorgis
1143 3a9fe2bc Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
1144 3a9fe2bc Dimitris Aragiorgis
                             ("/%s/instances/%s/snapshot" %
1145 3a9fe2bc Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), query, body)
1146 3a9fe2bc Dimitris Aragiorgis
1147 bfc2002f Michael Hanselmann
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1148 539d65ba Michael Hanselmann
                           remote_node=None, iallocator=None):
1149 95ab4de9 David Knowles
    """Replaces disks on an instance.
1150 95ab4de9 David Knowles

1151 95ab4de9 David Knowles
    @type instance: str
1152 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1153 bfc2002f Michael Hanselmann
    @type disks: list of ints
1154 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1155 95ab4de9 David Knowles
    @type mode: str
1156 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1157 95ab4de9 David Knowles
    @type remote_node: str or None
1158 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1159 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1160 95ab4de9 David Knowles
    @type iallocator: str or None
1161 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1162 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1163 95ab4de9 David Knowles

1164 98805538 Michael Hanselmann
    @rtype: string
1165 95ab4de9 David Knowles
    @return: job id
1166 95ab4de9 David Knowles

1167 95ab4de9 David Knowles
    """
1168 cfc03c54 Michael Hanselmann
    query = [
1169 cfc03c54 Michael Hanselmann
      ("mode", mode),
1170 cfc03c54 Michael Hanselmann
      ]
1171 95ab4de9 David Knowles
1172 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1173 539d65ba Michael Hanselmann
1174 539d65ba Michael Hanselmann
    if disks is not None:
1175 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1176 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1177 bfc2002f Michael Hanselmann
1178 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1179 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1180 bfc2002f Michael Hanselmann
1181 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1182 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1183 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1184 95ab4de9 David Knowles
1185 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1186 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1187 ebeb600f Michael Hanselmann

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

1195 ebeb600f Michael Hanselmann
    """
1196 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1197 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1198 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1199 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1200 ebeb600f Michael Hanselmann
1201 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1202 ebeb600f Michael Hanselmann
                     remove_instance=None,
1203 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1204 ebeb600f Michael Hanselmann
    """Exports an instance.
1205 ebeb600f Michael Hanselmann

1206 ebeb600f Michael Hanselmann
    @type instance: string
1207 ebeb600f Michael Hanselmann
    @param instance: Instance name
1208 ebeb600f Michael Hanselmann
    @type mode: string
1209 ebeb600f Michael Hanselmann
    @param mode: Export mode
1210 ebeb600f Michael Hanselmann
    @rtype: string
1211 ebeb600f Michael Hanselmann
    @return: Job ID
1212 ebeb600f Michael Hanselmann

1213 ebeb600f Michael Hanselmann
    """
1214 ebeb600f Michael Hanselmann
    body = {
1215 ebeb600f Michael Hanselmann
      "destination": destination,
1216 ebeb600f Michael Hanselmann
      "mode": mode,
1217 ebeb600f Michael Hanselmann
      }
1218 ebeb600f Michael Hanselmann
1219 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1220 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1221 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1222 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1223 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1224 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1225 ebeb600f Michael Hanselmann
1226 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1227 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1228 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1229 ebeb600f Michael Hanselmann
1230 dc700f75 Daniel Krambrock
  def MigrateInstance(self, instance, mode=None, cleanup=None,
1231 dc700f75 Daniel Krambrock
                      target_node=None):
1232 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1233 e0ac6ce6 Michael Hanselmann

1234 e0ac6ce6 Michael Hanselmann
    @type instance: string
1235 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1236 e0ac6ce6 Michael Hanselmann
    @type mode: string
1237 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1238 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1239 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1240 dc700f75 Daniel Krambrock
    @type target_node: string
1241 dc700f75 Daniel Krambrock
    @param target_node: Target Node for externally mirrored instances
1242 d914c76f Simeon Miteff
    @rtype: string
1243 d914c76f Simeon Miteff
    @return: job id
1244 e0ac6ce6 Michael Hanselmann

1245 e0ac6ce6 Michael Hanselmann
    """
1246 e0ac6ce6 Michael Hanselmann
    body = {}
1247 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1248 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1249 dc700f75 Daniel Krambrock
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1250 e0ac6ce6 Michael Hanselmann
1251 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1252 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1253 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1254 e0ac6ce6 Michael Hanselmann
1255 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1256 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1257 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1258 c0a146a1 Michael Hanselmann

1259 c0a146a1 Michael Hanselmann
    @type instance: string
1260 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1261 c0a146a1 Michael Hanselmann
    @type iallocator: string
1262 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1263 c0a146a1 Michael Hanselmann
      shared-storage instances
1264 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1265 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1266 c0a146a1 Michael Hanselmann
    @type target_node: string
1267 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1268 c0a146a1 Michael Hanselmann
    @rtype: string
1269 c0a146a1 Michael Hanselmann
    @return: job id
1270 c0a146a1 Michael Hanselmann

1271 c0a146a1 Michael Hanselmann
    """
1272 c0a146a1 Michael Hanselmann
    body = {}
1273 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1274 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1275 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1276 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1277 c0a146a1 Michael Hanselmann
1278 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1279 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1280 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1281 c0a146a1 Michael Hanselmann
1282 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1283 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1284 d654aae1 Michael Hanselmann

1285 d654aae1 Michael Hanselmann
    @type instance: string
1286 d654aae1 Michael Hanselmann
    @param instance: Instance name
1287 d654aae1 Michael Hanselmann
    @type new_name: string
1288 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1289 d654aae1 Michael Hanselmann
    @type ip_check: bool
1290 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1291 d654aae1 Michael Hanselmann
    @type name_check: bool
1292 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1293 d914c76f Simeon Miteff
    @rtype: string
1294 d914c76f Simeon Miteff
    @return: job id
1295 d654aae1 Michael Hanselmann

1296 d654aae1 Michael Hanselmann
    """
1297 d654aae1 Michael Hanselmann
    body = {
1298 d654aae1 Michael Hanselmann
      "new_name": new_name,
1299 d654aae1 Michael Hanselmann
      }
1300 d654aae1 Michael Hanselmann
1301 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1302 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1303 d654aae1 Michael Hanselmann
1304 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1305 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1306 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1307 d654aae1 Michael Hanselmann
1308 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1309 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1310 b82d4c5e Michael Hanselmann

1311 b82d4c5e Michael Hanselmann
    @type instance: string
1312 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1313 d914c76f Simeon Miteff
    @rtype: dict
1314 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1315 b82d4c5e Michael Hanselmann

1316 b82d4c5e Michael Hanselmann
    """
1317 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1318 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1319 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1320 b82d4c5e Michael Hanselmann
1321 3312709d Leon Handreke
  def GetJobs(self, bulk=False):
1322 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1323 95ab4de9 David Knowles

1324 3312709d Leon Handreke
    @type bulk: bool
1325 3312709d Leon Handreke
    @param bulk: Whether to return detailed information about jobs.
1326 95ab4de9 David Knowles
    @rtype: list of int
1327 3312709d Leon Handreke
    @return: List of job ids for the cluster or list of dicts with detailed
1328 3312709d Leon Handreke
             information about the jobs if bulk parameter was true.
1329 95ab4de9 David Knowles

1330 95ab4de9 David Knowles
    """
1331 3312709d Leon Handreke
    query = []
1332 3312709d Leon Handreke
    _AppendIf(query, bulk, ("bulk", 1))
1333 3312709d Leon Handreke
1334 3312709d Leon Handreke
    if bulk:
1335 3312709d Leon Handreke
      return self._SendRequest(HTTP_GET,
1336 3312709d Leon Handreke
                               "/%s/jobs" % GANETI_RAPI_VERSION,
1337 3312709d Leon Handreke
                               query, None)
1338 3312709d Leon Handreke
    else:
1339 3312709d Leon Handreke
      return [int(j["id"])
1340 3312709d Leon Handreke
              for j in self._SendRequest(HTTP_GET,
1341 3312709d Leon Handreke
                                         "/%s/jobs" % GANETI_RAPI_VERSION,
1342 3312709d Leon Handreke
                                         None, None)]
1343 95ab4de9 David Knowles
1344 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1345 95ab4de9 David Knowles
    """Gets the status of a job.
1346 95ab4de9 David Knowles

1347 98805538 Michael Hanselmann
    @type job_id: string
1348 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1349 95ab4de9 David Knowles

1350 95ab4de9 David Knowles
    @rtype: dict
1351 95ab4de9 David Knowles
    @return: job status
1352 95ab4de9 David Knowles

1353 95ab4de9 David Knowles
    """
1354 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1355 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1356 a198b2d9 Michael Hanselmann
                             None, None)
1357 95ab4de9 David Knowles
1358 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1359 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1360 16c13387 Theo Van Dinter

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

1364 cfda0e48 Iustin Pop
    @type job_id: string
1365 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1366 16c13387 Theo Van Dinter
    @type period: int
1367 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1368 16c13387 Theo Van Dinter
    @type retries: int
1369 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1370 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1371 16c13387 Theo Van Dinter

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

1378 16c13387 Theo Van Dinter
    """
1379 16c13387 Theo Van Dinter
    while retries != 0:
1380 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1381 dde0e97c Michael Hanselmann
1382 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1383 16c13387 Theo Van Dinter
        return True
1384 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1385 dde0e97c Michael Hanselmann
        return False
1386 dde0e97c Michael Hanselmann
1387 dde0e97c Michael Hanselmann
      if period:
1388 dde0e97c Michael Hanselmann
        time.sleep(period)
1389 dde0e97c Michael Hanselmann
1390 16c13387 Theo Van Dinter
      if retries > 0:
1391 16c13387 Theo Van Dinter
        retries -= 1
1392 dde0e97c Michael Hanselmann
1393 16c13387 Theo Van Dinter
    return False
1394 16c13387 Theo Van Dinter
1395 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1396 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1397 d9b67f70 Michael Hanselmann

1398 98805538 Michael Hanselmann
    @type job_id: string
1399 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1400 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1401 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1402 d914c76f Simeon Miteff
    @rtype: dict
1403 d9b67f70 Michael Hanselmann

1404 d9b67f70 Michael Hanselmann
    """
1405 d9b67f70 Michael Hanselmann
    body = {
1406 d9b67f70 Michael Hanselmann
      "fields": fields,
1407 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1408 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1409 d9b67f70 Michael Hanselmann
      }
1410 d9b67f70 Michael Hanselmann
1411 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1412 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1413 a198b2d9 Michael Hanselmann
                             None, body)
1414 d9b67f70 Michael Hanselmann
1415 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1416 cf9ada49 Michael Hanselmann
    """Cancels a job.
1417 95ab4de9 David Knowles

1418 98805538 Michael Hanselmann
    @type job_id: string
1419 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1420 95ab4de9 David Knowles
    @type dry_run: bool
1421 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1422 d914c76f Simeon Miteff
    @rtype: tuple
1423 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1424 95ab4de9 David Knowles

1425 95ab4de9 David Knowles
    """
1426 95ab4de9 David Knowles
    query = []
1427 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1428 95ab4de9 David Knowles
1429 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1430 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1431 a198b2d9 Michael Hanselmann
                             query, None)
1432 95ab4de9 David Knowles
1433 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1434 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1435 95ab4de9 David Knowles

1436 95ab4de9 David Knowles
    @type bulk: bool
1437 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1438 95ab4de9 David Knowles

1439 95ab4de9 David Knowles
    @rtype: list of dict or str
1440 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1441 95ab4de9 David Knowles
        else list of nodes in the cluster
1442 95ab4de9 David Knowles

1443 95ab4de9 David Knowles
    """
1444 95ab4de9 David Knowles
    query = []
1445 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1446 95ab4de9 David Knowles
1447 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1448 a198b2d9 Michael Hanselmann
                              query, None)
1449 95ab4de9 David Knowles
    if bulk:
1450 95ab4de9 David Knowles
      return nodes
1451 95ab4de9 David Knowles
    else:
1452 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1453 95ab4de9 David Knowles
1454 591e5103 Michael Hanselmann
  def GetNode(self, node):
1455 95ab4de9 David Knowles
    """Gets information about a node.
1456 95ab4de9 David Knowles

1457 95ab4de9 David Knowles
    @type node: str
1458 95ab4de9 David Knowles
    @param node: node whose info to return
1459 95ab4de9 David Knowles

1460 95ab4de9 David Knowles
    @rtype: dict
1461 95ab4de9 David Knowles
    @return: info about the node
1462 95ab4de9 David Knowles

1463 95ab4de9 David Knowles
    """
1464 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1465 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1466 a198b2d9 Michael Hanselmann
                             None, None)
1467 95ab4de9 David Knowles
1468 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1469 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1470 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1471 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1472 95ab4de9 David Knowles

1473 95ab4de9 David Knowles
    @type node: str
1474 95ab4de9 David Knowles
    @param node: node to evacuate
1475 95ab4de9 David Knowles
    @type iallocator: str or None
1476 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1477 95ab4de9 David Knowles
    @type remote_node: str
1478 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1479 95ab4de9 David Knowles
    @type dry_run: bool
1480 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1481 941b9309 Iustin Pop
    @type early_release: bool
1482 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1483 0b58db81 Michael Hanselmann
    @type mode: string
1484 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1485 de40437a Michael Hanselmann
    @type accept_old: bool
1486 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1487 de40437a Michael Hanselmann
        results
1488 de40437a Michael Hanselmann

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

1495 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1496 941b9309 Iustin Pop
        specified
1497 95ab4de9 David Knowles

1498 95ab4de9 David Knowles
    """
1499 95ab4de9 David Knowles
    if iallocator and remote_node:
1500 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1501 95ab4de9 David Knowles
1502 cfc03c54 Michael Hanselmann
    query = []
1503 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1504 de40437a Michael Hanselmann
1505 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1506 0b58db81 Michael Hanselmann
      # Server supports body parameters
1507 de40437a Michael Hanselmann
      body = {}
1508 de40437a Michael Hanselmann
1509 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1510 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1511 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1512 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1513 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1514 de40437a Michael Hanselmann
    else:
1515 de40437a Michael Hanselmann
      # Pre-2.5 request format
1516 de40437a Michael Hanselmann
      body = None
1517 de40437a Michael Hanselmann
1518 de40437a Michael Hanselmann
      if not accept_old:
1519 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1520 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1521 de40437a Michael Hanselmann
                             " accept_old)")
1522 de40437a Michael Hanselmann
1523 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1524 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1525 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1526 de40437a Michael Hanselmann
1527 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1528 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1529 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1530 95ab4de9 David Knowles
1531 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1532 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1533 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1534 95ab4de9 David Knowles
1535 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1536 b7a1c816 Michael Hanselmann
                  target_node=None):
1537 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1538 95ab4de9 David Knowles

1539 95ab4de9 David Knowles
    @type node: str
1540 95ab4de9 David Knowles
    @param node: node to migrate
1541 1f334d96 Iustin Pop
    @type mode: string
1542 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1543 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1544 95ab4de9 David Knowles
    @type dry_run: bool
1545 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1546 b7a1c816 Michael Hanselmann
    @type iallocator: string
1547 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1548 b7a1c816 Michael Hanselmann
    @type target_node: string
1549 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1550 95ab4de9 David Knowles

1551 98805538 Michael Hanselmann
    @rtype: string
1552 95ab4de9 David Knowles
    @return: job id
1553 95ab4de9 David Knowles

1554 95ab4de9 David Knowles
    """
1555 95ab4de9 David Knowles
    query = []
1556 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1557 95ab4de9 David Knowles
1558 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1559 b7a1c816 Michael Hanselmann
      body = {}
1560 b7a1c816 Michael Hanselmann
1561 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1562 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1563 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1564 b7a1c816 Michael Hanselmann
1565 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1566 b7a1c816 Michael Hanselmann
1567 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1568 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1569 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1570 b7a1c816 Michael Hanselmann
    else:
1571 b7a1c816 Michael Hanselmann
      # Use old request format
1572 b7a1c816 Michael Hanselmann
      if target_node is not None:
1573 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1574 b7a1c816 Michael Hanselmann
                             " for node migration")
1575 b7a1c816 Michael Hanselmann
1576 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1577 b7a1c816 Michael Hanselmann
1578 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1579 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1580 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1581 95ab4de9 David Knowles
1582 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1583 95ab4de9 David Knowles
    """Gets the current role for a node.
1584 95ab4de9 David Knowles

1585 95ab4de9 David Knowles
    @type node: str
1586 95ab4de9 David Knowles
    @param node: node whose role to return
1587 95ab4de9 David Knowles

1588 95ab4de9 David Knowles
    @rtype: str
1589 95ab4de9 David Knowles
    @return: the current role for a node
1590 95ab4de9 David Knowles

1591 95ab4de9 David Knowles
    """
1592 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1593 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1594 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1595 95ab4de9 David Knowles
1596 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1597 95ab4de9 David Knowles
    """Sets the role for a node.
1598 95ab4de9 David Knowles

1599 95ab4de9 David Knowles
    @type node: str
1600 95ab4de9 David Knowles
    @param node: the node whose role to set
1601 95ab4de9 David Knowles
    @type role: str
1602 95ab4de9 David Knowles
    @param role: the role to set for the node
1603 95ab4de9 David Knowles
    @type force: bool
1604 95ab4de9 David Knowles
    @param force: whether to force the role change
1605 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1606 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1607 8de8e68d Michael Hanselmann
                         if necessary
1608 95ab4de9 David Knowles

1609 98805538 Michael Hanselmann
    @rtype: string
1610 95ab4de9 David Knowles
    @return: job id
1611 95ab4de9 David Knowles

1612 95ab4de9 David Knowles
    """
1613 4c864b55 Michael Hanselmann
    query = []
1614 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1615 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1616 8de8e68d Michael Hanselmann
1617 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1618 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1619 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1620 95ab4de9 David Knowles
1621 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1622 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1623 42d4d8b9 Michael Hanselmann

1624 42d4d8b9 Michael Hanselmann
    @type node: string
1625 42d4d8b9 Michael Hanselmann
    @param node: Node name
1626 42d4d8b9 Michael Hanselmann
    @type force: bool
1627 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1628 42d4d8b9 Michael Hanselmann
    @rtype: string
1629 42d4d8b9 Michael Hanselmann
    @return: job id
1630 42d4d8b9 Michael Hanselmann

1631 42d4d8b9 Michael Hanselmann
    """
1632 4c864b55 Michael Hanselmann
    query = []
1633 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1634 42d4d8b9 Michael Hanselmann
1635 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1636 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1637 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1638 42d4d8b9 Michael Hanselmann
1639 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1640 94497dd1 Michael Hanselmann
    """Modifies a node.
1641 94497dd1 Michael Hanselmann

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

1644 370f2042 Guido Trotter
    @type node: string
1645 370f2042 Guido Trotter
    @param node: Node name
1646 94497dd1 Michael Hanselmann
    @rtype: string
1647 94497dd1 Michael Hanselmann
    @return: job id
1648 94497dd1 Michael Hanselmann

1649 94497dd1 Michael Hanselmann
    """
1650 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1651 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1652 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1653 94497dd1 Michael Hanselmann
1654 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1655 95ab4de9 David Knowles
    """Gets the storage units for a node.
1656 95ab4de9 David Knowles

1657 95ab4de9 David Knowles
    @type node: str
1658 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1659 95ab4de9 David Knowles
    @type storage_type: str
1660 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1661 95ab4de9 David Knowles
    @type output_fields: str
1662 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1663 95ab4de9 David Knowles

1664 98805538 Michael Hanselmann
    @rtype: string
1665 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1666 95ab4de9 David Knowles

1667 95ab4de9 David Knowles
    """
1668 cfc03c54 Michael Hanselmann
    query = [
1669 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1670 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1671 cfc03c54 Michael Hanselmann
      ]
1672 95ab4de9 David Knowles
1673 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1674 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1675 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1676 95ab4de9 David Knowles
1677 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1678 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1679 95ab4de9 David Knowles

1680 95ab4de9 David Knowles
    @type node: str
1681 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1682 95ab4de9 David Knowles
    @type storage_type: str
1683 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1684 95ab4de9 David Knowles
    @type name: str
1685 95ab4de9 David Knowles
    @param name: name of the storage unit
1686 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1687 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1688 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1689 95ab4de9 David Knowles

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

1693 95ab4de9 David Knowles
    """
1694 95ab4de9 David Knowles
    query = [
1695 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1696 cfc03c54 Michael Hanselmann
      ("name", name),
1697 cfc03c54 Michael Hanselmann
      ]
1698 cfc03c54 Michael Hanselmann
1699 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1700 fde28316 Michael Hanselmann
1701 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1702 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1703 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1704 95ab4de9 David Knowles
1705 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1706 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1707 95ab4de9 David Knowles

1708 95ab4de9 David Knowles
    @type node: str
1709 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1710 95ab4de9 David Knowles
    @type storage_type: str
1711 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1712 95ab4de9 David Knowles
    @type name: str
1713 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1714 95ab4de9 David Knowles

1715 98805538 Michael Hanselmann
    @rtype: string
1716 95ab4de9 David Knowles
    @return: job id
1717 95ab4de9 David Knowles

1718 95ab4de9 David Knowles
    """
1719 cfc03c54 Michael Hanselmann
    query = [
1720 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1721 cfc03c54 Michael Hanselmann
      ("name", name),
1722 cfc03c54 Michael Hanselmann
      ]
1723 95ab4de9 David Knowles
1724 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1725 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1726 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1727 95ab4de9 David Knowles
1728 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1729 95ab4de9 David Knowles
    """Gets the tags for a node.
1730 95ab4de9 David Knowles

1731 95ab4de9 David Knowles
    @type node: str
1732 95ab4de9 David Knowles
    @param node: node whose tags to return
1733 95ab4de9 David Knowles

1734 95ab4de9 David Knowles
    @rtype: list of str
1735 95ab4de9 David Knowles
    @return: tags for the node
1736 95ab4de9 David Knowles

1737 95ab4de9 David Knowles
    """
1738 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1739 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1740 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1741 95ab4de9 David Knowles
1742 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1743 95ab4de9 David Knowles
    """Adds tags to a node.
1744 95ab4de9 David Knowles

1745 95ab4de9 David Knowles
    @type node: str
1746 95ab4de9 David Knowles
    @param node: node to add tags to
1747 95ab4de9 David Knowles
    @type tags: list of str
1748 95ab4de9 David Knowles
    @param tags: tags to add to the node
1749 95ab4de9 David Knowles
    @type dry_run: bool
1750 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1751 95ab4de9 David Knowles

1752 98805538 Michael Hanselmann
    @rtype: string
1753 95ab4de9 David Knowles
    @return: job id
1754 95ab4de9 David Knowles

1755 95ab4de9 David Knowles
    """
1756 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1757 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1758 95ab4de9 David Knowles
1759 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1760 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1761 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1762 95ab4de9 David Knowles
1763 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1764 95ab4de9 David Knowles
    """Delete tags from a node.
1765 95ab4de9 David Knowles

1766 95ab4de9 David Knowles
    @type node: str
1767 95ab4de9 David Knowles
    @param node: node to remove tags from
1768 95ab4de9 David Knowles
    @type tags: list of str
1769 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1770 95ab4de9 David Knowles
    @type dry_run: bool
1771 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1772 95ab4de9 David Knowles

1773 98805538 Michael Hanselmann
    @rtype: string
1774 95ab4de9 David Knowles
    @return: job id
1775 95ab4de9 David Knowles

1776 95ab4de9 David Knowles
    """
1777 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1778 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1779 95ab4de9 David Knowles
1780 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1781 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1782 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1783 a268af8d Adeodato Simo
1784 4588b4bd Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1785 4588b4bd Dimitris Aragiorgis
    """Gets all networks in the cluster.
1786 4588b4bd Dimitris Aragiorgis

1787 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1788 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1789 4588b4bd Dimitris Aragiorgis

1790 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1791 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1792 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1793 4588b4bd Dimitris Aragiorgis

1794 4588b4bd Dimitris Aragiorgis
    """
1795 4588b4bd Dimitris Aragiorgis
    query = []
1796 4588b4bd Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1797 4588b4bd Dimitris Aragiorgis
1798 4588b4bd Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1799 3c286190 Dimitris Aragiorgis
                                 query, None)
1800 4588b4bd Dimitris Aragiorgis
    if bulk:
1801 4588b4bd Dimitris Aragiorgis
      return networks
1802 4588b4bd Dimitris Aragiorgis
    else:
1803 4588b4bd Dimitris Aragiorgis
      return [n["name"] for n in networks]
1804 4588b4bd Dimitris Aragiorgis
1805 4588b4bd Dimitris Aragiorgis
  def GetNetwork(self, network):
1806 4588b4bd Dimitris Aragiorgis
    """Gets information about a network.
1807 4588b4bd Dimitris Aragiorgis

1808 1fa2c40b Dimitris Aragiorgis
    @type network: str
1809 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1810 4588b4bd Dimitris Aragiorgis

1811 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1812 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1813 4588b4bd Dimitris Aragiorgis

1814 4588b4bd Dimitris Aragiorgis
    """
1815 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1816 4588b4bd Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1817 4588b4bd Dimitris Aragiorgis
                             None, None)
1818 4588b4bd Dimitris Aragiorgis
1819 4588b4bd Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1820 5cfa6c37 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None,
1821 8140e24f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=None, dry_run=False):
1822 4588b4bd Dimitris Aragiorgis
    """Creates a new network.
1823 4588b4bd Dimitris Aragiorgis

1824 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1825 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1826 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1827 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1828 4588b4bd Dimitris Aragiorgis

1829 4588b4bd Dimitris Aragiorgis
    @rtype: string
1830 4588b4bd Dimitris Aragiorgis
    @return: job id
1831 4588b4bd Dimitris Aragiorgis

1832 4588b4bd Dimitris Aragiorgis
    """
1833 4588b4bd Dimitris Aragiorgis
    query = []
1834 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1835 4588b4bd Dimitris Aragiorgis
1836 8140e24f Dimitris Aragiorgis
    if add_reserved_ips:
1837 2556424d Iustin Pop
      add_reserved_ips = add_reserved_ips.split(",")
1838 8140e24f Dimitris Aragiorgis
1839 8140e24f Dimitris Aragiorgis
    if tags:
1840 2556424d Iustin Pop
      tags = tags.split(",")
1841 8140e24f Dimitris Aragiorgis
1842 4588b4bd Dimitris Aragiorgis
    body = {
1843 4588b4bd Dimitris Aragiorgis
      "network_name": network_name,
1844 4588b4bd Dimitris Aragiorgis
      "gateway": gateway,
1845 4588b4bd Dimitris Aragiorgis
      "network": network,
1846 4588b4bd Dimitris Aragiorgis
      "gateway6": gateway6,
1847 4588b4bd Dimitris Aragiorgis
      "network6": network6,
1848 4588b4bd Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1849 8140e24f Dimitris Aragiorgis
      "add_reserved_ips": add_reserved_ips,
1850 8140e24f Dimitris Aragiorgis
      "tags": tags,
1851 4588b4bd Dimitris Aragiorgis
      }
1852 4588b4bd Dimitris Aragiorgis
1853 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1854 4588b4bd Dimitris Aragiorgis
                             query, body)
1855 4588b4bd Dimitris Aragiorgis
1856 6e8091f9 Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link, dry_run=False):
1857 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1858 4588b4bd Dimitris Aragiorgis

1859 4588b4bd Dimitris Aragiorgis
    """
1860 4588b4bd Dimitris Aragiorgis
    body = {
1861 4588b4bd Dimitris Aragiorgis
      "group_name": group_name,
1862 4588b4bd Dimitris Aragiorgis
      "network_mode": mode,
1863 3c286190 Dimitris Aragiorgis
      "network_link": link,
1864 4588b4bd Dimitris Aragiorgis
      }
1865 4588b4bd Dimitris Aragiorgis
1866 6e8091f9 Dimitris Aragiorgis
    query = []
1867 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1868 6e8091f9 Dimitris Aragiorgis
1869 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1870 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1871 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1872 4588b4bd Dimitris Aragiorgis
1873 6e8091f9 Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name, dry_run=False):
1874 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1875 4588b4bd Dimitris Aragiorgis

1876 4588b4bd Dimitris Aragiorgis
    """
1877 4588b4bd Dimitris Aragiorgis
    body = {
1878 3c286190 Dimitris Aragiorgis
      "group_name": group_name,
1879 4588b4bd Dimitris Aragiorgis
      }
1880 6e8091f9 Dimitris Aragiorgis
1881 6e8091f9 Dimitris Aragiorgis
    query = []
1882 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1883 6e8091f9 Dimitris Aragiorgis
1884 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1885 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1886 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1887 6e8091f9 Dimitris Aragiorgis
1888 6e8091f9 Dimitris Aragiorgis
  def ModifyNetwork(self, network, **kwargs):
1889 6e8091f9 Dimitris Aragiorgis
    """Modifies a network.
1890 6e8091f9 Dimitris Aragiorgis

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

1893 6e8091f9 Dimitris Aragiorgis
    @type network: string
1894 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1895 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1896 6e8091f9 Dimitris Aragiorgis
    @return: job id
1897 4588b4bd Dimitris Aragiorgis

1898 6e8091f9 Dimitris Aragiorgis
    """
1899 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1900 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1901 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1902 4588b4bd Dimitris Aragiorgis
1903 4588b4bd Dimitris Aragiorgis
  def DeleteNetwork(self, network, dry_run=False):
1904 4588b4bd Dimitris Aragiorgis
    """Deletes a network.
1905 4588b4bd Dimitris Aragiorgis

1906 1fa2c40b Dimitris Aragiorgis
    @type network: str
1907 1fa2c40b Dimitris Aragiorgis
    @param network: the network to delete
1908 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1909 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1910 4588b4bd Dimitris Aragiorgis

1911 4588b4bd Dimitris Aragiorgis
    @rtype: string
1912 4588b4bd Dimitris Aragiorgis
    @return: job id
1913 4588b4bd Dimitris Aragiorgis

1914 4588b4bd Dimitris Aragiorgis
    """
1915 4588b4bd Dimitris Aragiorgis
    query = []
1916 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1917 4588b4bd Dimitris Aragiorgis
1918 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1919 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1920 4588b4bd Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1921 4588b4bd Dimitris Aragiorgis
1922 6e8091f9 Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1923 6e8091f9 Dimitris Aragiorgis
    """Gets tags for a network.
1924 6e8091f9 Dimitris Aragiorgis

1925 6e8091f9 Dimitris Aragiorgis
    @type network: string
1926 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1927 6e8091f9 Dimitris Aragiorgis

1928 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1929 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1930 6e8091f9 Dimitris Aragiorgis

1931 6e8091f9 Dimitris Aragiorgis
    """
1932 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1933 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1934 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1935 6e8091f9 Dimitris Aragiorgis
1936 6e8091f9 Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1937 6e8091f9 Dimitris Aragiorgis
    """Adds tags to a network.
1938 6e8091f9 Dimitris Aragiorgis

1939 6e8091f9 Dimitris Aragiorgis
    @type network: str
1940 6e8091f9 Dimitris Aragiorgis
    @param network: network to add tags to
1941 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1942 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to add to the network
1943 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1944 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1945 6e8091f9 Dimitris Aragiorgis

1946 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1947 6e8091f9 Dimitris Aragiorgis
    @return: job id
1948 6e8091f9 Dimitris Aragiorgis

1949 6e8091f9 Dimitris Aragiorgis
    """
1950 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1951 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1952 6e8091f9 Dimitris Aragiorgis
1953 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1954 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1955 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1956 6e8091f9 Dimitris Aragiorgis
1957 6e8091f9 Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1958 6e8091f9 Dimitris Aragiorgis
    """Deletes tags from a network.
1959 6e8091f9 Dimitris Aragiorgis

1960 6e8091f9 Dimitris Aragiorgis
    @type network: str
1961 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1962 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1963 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
1964 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1965 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1966 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1967 6e8091f9 Dimitris Aragiorgis
    @return: job id
1968 6e8091f9 Dimitris Aragiorgis

1969 6e8091f9 Dimitris Aragiorgis
    """
1970 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1971 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1972 6e8091f9 Dimitris Aragiorgis
1973 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1974 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1975 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1976 6e8091f9 Dimitris Aragiorgis
1977 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1978 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1979 a268af8d Adeodato Simo

1980 a268af8d Adeodato Simo
    @type bulk: bool
1981 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1982 a268af8d Adeodato Simo

1983 a268af8d Adeodato Simo
    @rtype: list of dict or str
1984 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1985 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1986 a268af8d Adeodato Simo

1987 a268af8d Adeodato Simo
    """
1988 a268af8d Adeodato Simo
    query = []
1989 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1990 a268af8d Adeodato Simo
1991 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1992 a268af8d Adeodato Simo
                               query, None)
1993 a268af8d Adeodato Simo
    if bulk:
1994 a268af8d Adeodato Simo
      return groups
1995 a268af8d Adeodato Simo
    else:
1996 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1997 a268af8d Adeodato Simo
1998 a268af8d Adeodato Simo
  def GetGroup(self, group):
1999 a268af8d Adeodato Simo
    """Gets information about a node group.
2000 a268af8d Adeodato Simo

2001 a268af8d Adeodato Simo
    @type group: str
2002 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
2003 a268af8d Adeodato Simo

2004 a268af8d Adeodato Simo
    @rtype: dict
2005 a268af8d Adeodato Simo
    @return: info about the node group
2006 a268af8d Adeodato Simo

2007 a268af8d Adeodato Simo
    """
2008 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
2009 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
2010 a268af8d Adeodato Simo
                             None, None)
2011 a268af8d Adeodato Simo
2012 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
2013 a268af8d Adeodato Simo
    """Creates a new node group.
2014 a268af8d Adeodato Simo

2015 a268af8d Adeodato Simo
    @type name: str
2016 a268af8d Adeodato Simo
    @param name: the name of node group to create
2017 90e99856 Adeodato Simo
    @type alloc_policy: str
2018 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
2019 a268af8d Adeodato Simo
    @type dry_run: bool
2020 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2021 a268af8d Adeodato Simo

2022 98805538 Michael Hanselmann
    @rtype: string
2023 a268af8d Adeodato Simo
    @return: job id
2024 a268af8d Adeodato Simo

2025 a268af8d Adeodato Simo
    """
2026 a268af8d Adeodato Simo
    query = []
2027 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2028 a268af8d Adeodato Simo
2029 a268af8d Adeodato Simo
    body = {
2030 a268af8d Adeodato Simo
      "name": name,
2031 3c286190 Dimitris Aragiorgis
      "alloc_policy": alloc_policy,
2032 a268af8d Adeodato Simo
      }
2033 a268af8d Adeodato Simo
2034 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
2035 a268af8d Adeodato Simo
                             query, body)
2036 a268af8d Adeodato Simo
2037 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
2038 f18fab7d Adeodato Simo
    """Modifies a node group.
2039 f18fab7d Adeodato Simo

2040 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
2041 f18fab7d Adeodato Simo

2042 f18fab7d Adeodato Simo
    @type group: string
2043 f18fab7d Adeodato Simo
    @param group: Node group name
2044 98805538 Michael Hanselmann
    @rtype: string
2045 f18fab7d Adeodato Simo
    @return: job id
2046 f18fab7d Adeodato Simo

2047 f18fab7d Adeodato Simo
    """
2048 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2049 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
2050 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
2051 f18fab7d Adeodato Simo
2052 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
2053 a268af8d Adeodato Simo
    """Deletes a node group.
2054 a268af8d Adeodato Simo

2055 a268af8d Adeodato Simo
    @type group: str
2056 a268af8d Adeodato Simo
    @param group: the node group to delete
2057 a268af8d Adeodato Simo
    @type dry_run: bool
2058 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2059 a268af8d Adeodato Simo

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

2063 a268af8d Adeodato Simo
    """
2064 a268af8d Adeodato Simo
    query = []
2065 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2066 a268af8d Adeodato Simo
2067 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
2068 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
2069 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
2070 a268af8d Adeodato Simo
2071 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
2072 a268af8d Adeodato Simo
    """Changes the name of a node group.
2073 a268af8d Adeodato Simo

2074 a268af8d Adeodato Simo
    @type group: string
2075 a268af8d Adeodato Simo
    @param group: Node group name
2076 a268af8d Adeodato Simo
    @type new_name: string
2077 a268af8d Adeodato Simo
    @param new_name: New node group name
2078 a268af8d Adeodato Simo

2079 98805538 Michael Hanselmann
    @rtype: string
2080 a268af8d Adeodato Simo
    @return: job id
2081 a268af8d Adeodato Simo

2082 a268af8d Adeodato Simo
    """
2083 a268af8d Adeodato Simo
    body = {
2084 a268af8d Adeodato Simo
      "new_name": new_name,
2085 a268af8d Adeodato Simo
      }
2086 a268af8d Adeodato Simo
2087 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2088 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
2089 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
2090 4245446f Adeodato Simo
2091 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
2092 4245446f Adeodato Simo
    """Assigns nodes to a group.
2093 4245446f Adeodato Simo

2094 4245446f Adeodato Simo
    @type group: string
2095 6ce90543 Michael Hanselmann
    @param group: Node group name
2096 4245446f Adeodato Simo
    @type nodes: list of strings
2097 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
2098 4245446f Adeodato Simo

2099 98805538 Michael Hanselmann
    @rtype: string
2100 4245446f Adeodato Simo
    @return: job id
2101 4245446f Adeodato Simo

2102 4245446f Adeodato Simo
    """
2103 4245446f Adeodato Simo
    query = []
2104 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
2105 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2106 4245446f Adeodato Simo
2107 4245446f Adeodato Simo
    body = {
2108 4245446f Adeodato Simo
      "nodes": nodes,
2109 4245446f Adeodato Simo
      }
2110 4245446f Adeodato Simo
2111 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2112 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
2113 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
2114 208a6cff Michael Hanselmann
2115 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
2116 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
2117 414ebaf1 Michael Hanselmann

2118 414ebaf1 Michael Hanselmann
    @type group: string
2119 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2120 414ebaf1 Michael Hanselmann

2121 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2122 414ebaf1 Michael Hanselmann
    @return: tags for the group
2123 414ebaf1 Michael Hanselmann

2124 414ebaf1 Michael Hanselmann
    """
2125 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2126 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2127 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
2128 414ebaf1 Michael Hanselmann
2129 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
2130 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
2131 414ebaf1 Michael Hanselmann

2132 414ebaf1 Michael Hanselmann
    @type group: str
2133 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
2134 414ebaf1 Michael Hanselmann
    @type tags: list of string
2135 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
2136 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2137 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2138 414ebaf1 Michael Hanselmann

2139 414ebaf1 Michael Hanselmann
    @rtype: string
2140 414ebaf1 Michael Hanselmann
    @return: job id
2141 414ebaf1 Michael Hanselmann

2142 414ebaf1 Michael Hanselmann
    """
2143 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2144 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2145 414ebaf1 Michael Hanselmann
2146 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2147 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2148 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2149 414ebaf1 Michael Hanselmann
2150 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
2151 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
2152 414ebaf1 Michael Hanselmann

2153 414ebaf1 Michael Hanselmann
    @type group: str
2154 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2155 414ebaf1 Michael Hanselmann
    @type tags: list of string
2156 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
2157 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2158 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2159 414ebaf1 Michael Hanselmann
    @rtype: string
2160 414ebaf1 Michael Hanselmann
    @return: job id
2161 414ebaf1 Michael Hanselmann

2162 414ebaf1 Michael Hanselmann
    """
2163 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2164 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2165 414ebaf1 Michael Hanselmann
2166 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
2167 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2168 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2169 414ebaf1 Michael Hanselmann
2170 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
2171 208a6cff Michael Hanselmann
    """Retrieves information about resources.
2172 208a6cff Michael Hanselmann

2173 208a6cff Michael Hanselmann
    @type what: string
2174 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2175 208a6cff Michael Hanselmann
    @type fields: list of string
2176 208a6cff Michael Hanselmann
    @param fields: Requested fields
2177 2e5c33db Iustin Pop
    @type qfilter: None or list
2178 2e5c33db Iustin Pop
    @param qfilter: Query filter
2179 208a6cff Michael Hanselmann

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

2183 208a6cff Michael Hanselmann
    """
2184 208a6cff Michael Hanselmann
    body = {
2185 208a6cff Michael Hanselmann
      "fields": fields,
2186 208a6cff Michael Hanselmann
      }
2187 208a6cff Michael Hanselmann
2188 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
2189 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
2190 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
2191 208a6cff Michael Hanselmann
2192 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2193 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
2194 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
2195 208a6cff Michael Hanselmann
2196 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
2197 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
2198 208a6cff Michael Hanselmann

2199 208a6cff Michael Hanselmann
    @type what: string
2200 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2201 208a6cff Michael Hanselmann
    @type fields: list of string
2202 208a6cff Michael Hanselmann
    @param fields: Requested fields
2203 208a6cff Michael Hanselmann

2204 208a6cff Michael Hanselmann
    @rtype: string
2205 208a6cff Michael Hanselmann
    @return: job id
2206 208a6cff Michael Hanselmann

2207 208a6cff Michael Hanselmann
    """
2208 208a6cff Michael Hanselmann
    query = []
2209 208a6cff Michael Hanselmann
2210 208a6cff Michael Hanselmann
    if fields is not None:
2211 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
2212 208a6cff Michael Hanselmann
2213 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2214 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
2215 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)