Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 11eb5cd0

History | View | Annotate | Download (62.6 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1199 e0ac6ce6 Michael Hanselmann
    @type instance: string
1200 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1201 e0ac6ce6 Michael Hanselmann
    @type mode: string
1202 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1203 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1204 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1205 d914c76f Simeon Miteff
    @rtype: string
1206 d914c76f Simeon Miteff
    @return: job id
1207 e0ac6ce6 Michael Hanselmann

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

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

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

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

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

1273 b82d4c5e Michael Hanselmann
    @type instance: string
1274 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1275 d914c76f Simeon Miteff
    @rtype: dict
1276 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1277 b82d4c5e Michael Hanselmann

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

1286 95ab4de9 David Knowles
    @rtype: list of int
1287 95ab4de9 David Knowles
    @return: job ids for the cluster
1288 95ab4de9 David Knowles

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

1298 98805538 Michael Hanselmann
    @type job_id: string
1299 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1300 95ab4de9 David Knowles

1301 95ab4de9 David Knowles
    @rtype: dict
1302 95ab4de9 David Knowles
    @return: job status
1303 95ab4de9 David Knowles

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

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

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

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

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

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

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

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

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

1387 95ab4de9 David Knowles
    @type bulk: bool
1388 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1389 95ab4de9 David Knowles

1390 95ab4de9 David Knowles
    @rtype: list of dict or str
1391 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1392 95ab4de9 David Knowles
        else list of nodes in the cluster
1393 95ab4de9 David Knowles

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

1408 95ab4de9 David Knowles
    @type node: str
1409 95ab4de9 David Knowles
    @param node: node whose info to return
1410 95ab4de9 David Knowles

1411 95ab4de9 David Knowles
    @rtype: dict
1412 95ab4de9 David Knowles
    @return: info about the node
1413 95ab4de9 David Knowles

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

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

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

1446 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1447 941b9309 Iustin Pop
        specified
1448 95ab4de9 David Knowles

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

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

1502 98805538 Michael Hanselmann
    @rtype: string
1503 95ab4de9 David Knowles
    @return: job id
1504 95ab4de9 David Knowles

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

1536 95ab4de9 David Knowles
    @type node: str
1537 95ab4de9 David Knowles
    @param node: node whose role to return
1538 95ab4de9 David Knowles

1539 95ab4de9 David Knowles
    @rtype: str
1540 95ab4de9 David Knowles
    @return: the current role for a node
1541 95ab4de9 David Knowles

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

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

1560 98805538 Michael Hanselmann
    @rtype: string
1561 95ab4de9 David Knowles
    @return: job id
1562 95ab4de9 David Knowles

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

1575 42d4d8b9 Michael Hanselmann
    @type node: string
1576 42d4d8b9 Michael Hanselmann
    @param node: Node name
1577 42d4d8b9 Michael Hanselmann
    @type force: bool
1578 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1579 42d4d8b9 Michael Hanselmann
    @rtype: string
1580 42d4d8b9 Michael Hanselmann
    @return: job id
1581 42d4d8b9 Michael Hanselmann

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

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

1595 370f2042 Guido Trotter
    @type node: string
1596 370f2042 Guido Trotter
    @param node: Node name
1597 94497dd1 Michael Hanselmann
    @rtype: string
1598 94497dd1 Michael Hanselmann
    @return: job id
1599 94497dd1 Michael Hanselmann

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

1608 95ab4de9 David Knowles
    @type node: str
1609 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1610 95ab4de9 David Knowles
    @type storage_type: str
1611 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1612 95ab4de9 David Knowles
    @type output_fields: str
1613 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1614 95ab4de9 David Knowles

1615 98805538 Michael Hanselmann
    @rtype: string
1616 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1617 95ab4de9 David Knowles

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

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

1641 98805538 Michael Hanselmann
    @rtype: string
1642 95ab4de9 David Knowles
    @return: job id
1643 95ab4de9 David Knowles

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

1659 95ab4de9 David Knowles
    @type node: str
1660 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1661 95ab4de9 David Knowles
    @type storage_type: str
1662 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1663 95ab4de9 David Knowles
    @type name: str
1664 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1665 95ab4de9 David Knowles

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

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

1682 95ab4de9 David Knowles
    @type node: str
1683 95ab4de9 David Knowles
    @param node: node whose tags to return
1684 95ab4de9 David Knowles

1685 95ab4de9 David Knowles
    @rtype: list of str
1686 95ab4de9 David Knowles
    @return: tags for the node
1687 95ab4de9 David Knowles

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

1696 95ab4de9 David Knowles
    @type node: str
1697 95ab4de9 David Knowles
    @param node: node to add tags to
1698 95ab4de9 David Knowles
    @type tags: list of str
1699 95ab4de9 David Knowles
    @param tags: tags to add to the node
1700 95ab4de9 David Knowles
    @type dry_run: bool
1701 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1702 95ab4de9 David Knowles

1703 98805538 Michael Hanselmann
    @rtype: string
1704 95ab4de9 David Knowles
    @return: job id
1705 95ab4de9 David Knowles

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

1717 95ab4de9 David Knowles
    @type node: str
1718 95ab4de9 David Knowles
    @param node: node to remove tags from
1719 95ab4de9 David Knowles
    @type tags: list of str
1720 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1721 95ab4de9 David Knowles
    @type dry_run: bool
1722 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1723 95ab4de9 David Knowles

1724 98805538 Michael Hanselmann
    @rtype: string
1725 95ab4de9 David Knowles
    @return: job id
1726 95ab4de9 David Knowles

1727 95ab4de9 David Knowles
    """
1728 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1729 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1730 95ab4de9 David Knowles
1731 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1732 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1733 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1734 a268af8d Adeodato Simo
1735 4588b4bd Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1736 4588b4bd Dimitris Aragiorgis
    """Gets all networks in the cluster.
1737 4588b4bd Dimitris Aragiorgis

1738 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1739 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1740 4588b4bd Dimitris Aragiorgis

1741 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1742 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1743 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1744 4588b4bd Dimitris Aragiorgis

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

1759 1fa2c40b Dimitris Aragiorgis
    @type network: str
1760 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1761 4588b4bd Dimitris Aragiorgis

1762 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1763 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1764 4588b4bd Dimitris Aragiorgis

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

1775 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1776 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1777 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1778 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1779 4588b4bd Dimitris Aragiorgis

1780 4588b4bd Dimitris Aragiorgis
    @rtype: string
1781 4588b4bd Dimitris Aragiorgis
    @return: job id
1782 4588b4bd Dimitris Aragiorgis

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

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

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

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

1845 6e8091f9 Dimitris Aragiorgis
    @type network: string
1846 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1847 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1848 6e8091f9 Dimitris Aragiorgis
    @return: job id
1849 4588b4bd Dimitris Aragiorgis

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

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

1863 4588b4bd Dimitris Aragiorgis
    @rtype: string
1864 4588b4bd Dimitris Aragiorgis
    @return: job id
1865 4588b4bd Dimitris Aragiorgis

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

1877 6e8091f9 Dimitris Aragiorgis
    @type network: string
1878 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1879 6e8091f9 Dimitris Aragiorgis

1880 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1881 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1882 6e8091f9 Dimitris Aragiorgis

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

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

1898 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1899 6e8091f9 Dimitris Aragiorgis
    @return: job id
1900 6e8091f9 Dimitris Aragiorgis

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

1912 6e8091f9 Dimitris Aragiorgis
    @type network: str
1913 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1914 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1915 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
1916 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1917 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1918 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1919 6e8091f9 Dimitris Aragiorgis
    @return: job id
1920 6e8091f9 Dimitris Aragiorgis

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

1932 a268af8d Adeodato Simo
    @type bulk: bool
1933 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1934 a268af8d Adeodato Simo

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

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

1953 a268af8d Adeodato Simo
    @type group: str
1954 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1955 a268af8d Adeodato Simo

1956 a268af8d Adeodato Simo
    @rtype: dict
1957 a268af8d Adeodato Simo
    @return: info about the node group
1958 a268af8d Adeodato Simo

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

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

1974 98805538 Michael Hanselmann
    @rtype: string
1975 a268af8d Adeodato Simo
    @return: job id
1976 a268af8d Adeodato Simo

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

1992 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
1993 f18fab7d Adeodato Simo

1994 f18fab7d Adeodato Simo
    @type group: string
1995 f18fab7d Adeodato Simo
    @param group: Node group name
1996 98805538 Michael Hanselmann
    @rtype: string
1997 f18fab7d Adeodato Simo
    @return: job id
1998 f18fab7d Adeodato Simo

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

2007 a268af8d Adeodato Simo
    @type group: str
2008 a268af8d Adeodato Simo
    @param group: the node group to delete
2009 a268af8d Adeodato Simo
    @type dry_run: bool
2010 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
2011 a268af8d Adeodato Simo

2012 98805538 Michael Hanselmann
    @rtype: string
2013 a268af8d Adeodato Simo
    @return: job id
2014 a268af8d Adeodato Simo

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

2026 a268af8d Adeodato Simo
    @type group: string
2027 a268af8d Adeodato Simo
    @param group: Node group name
2028 a268af8d Adeodato Simo
    @type new_name: string
2029 a268af8d Adeodato Simo
    @param new_name: New node group name
2030 a268af8d Adeodato Simo

2031 98805538 Michael Hanselmann
    @rtype: string
2032 a268af8d Adeodato Simo
    @return: job id
2033 a268af8d Adeodato Simo

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

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

2051 98805538 Michael Hanselmann
    @rtype: string
2052 4245446f Adeodato Simo
    @return: job id
2053 4245446f Adeodato Simo

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

2070 414ebaf1 Michael Hanselmann
    @type group: string
2071 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2072 414ebaf1 Michael Hanselmann

2073 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2074 414ebaf1 Michael Hanselmann
    @return: tags for the group
2075 414ebaf1 Michael Hanselmann

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

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

2091 414ebaf1 Michael Hanselmann
    @rtype: string
2092 414ebaf1 Michael Hanselmann
    @return: job id
2093 414ebaf1 Michael Hanselmann

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

2105 414ebaf1 Michael Hanselmann
    @type group: str
2106 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2107 414ebaf1 Michael Hanselmann
    @type tags: list of string
2108 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
2109 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2110 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2111 414ebaf1 Michael Hanselmann
    @rtype: string
2112 414ebaf1 Michael Hanselmann
    @return: job id
2113 414ebaf1 Michael Hanselmann

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

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

2132 208a6cff Michael Hanselmann
    @rtype: string
2133 208a6cff Michael Hanselmann
    @return: job id
2134 208a6cff Michael Hanselmann

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

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

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

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