Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 6ce90543

History | View | Annotate | Download (54.5 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 63d5eb8a Michael Hanselmann
JOB_STATUS_FINALIZED = frozenset([
84 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELED,
85 63d5eb8a Michael Hanselmann
  JOB_STATUS_SUCCESS,
86 63d5eb8a Michael Hanselmann
  JOB_STATUS_ERROR,
87 63d5eb8a Michael Hanselmann
  ])
88 63d5eb8a Michael Hanselmann
JOB_STATUS_ALL = frozenset([
89 63d5eb8a Michael Hanselmann
  JOB_STATUS_QUEUED,
90 47099cd1 Michael Hanselmann
  JOB_STATUS_WAITING,
91 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELING,
92 63d5eb8a Michael Hanselmann
  JOB_STATUS_RUNNING,
93 63d5eb8a Michael Hanselmann
  ]) | JOB_STATUS_FINALIZED
94 63d5eb8a Michael Hanselmann
95 47099cd1 Michael Hanselmann
# Legacy name
96 47099cd1 Michael Hanselmann
JOB_STATUS_WAITLOCK = JOB_STATUS_WAITING
97 47099cd1 Michael Hanselmann
98 8a47b447 Michael Hanselmann
# Internal constants
99 8a47b447 Michael Hanselmann
_REQ_DATA_VERSION_FIELD = "__version__"
100 4c864b55 Michael Hanselmann
_QPARAM_DRY_RUN = "dry-run"
101 4c864b55 Michael Hanselmann
_QPARAM_FORCE = "force"
102 8a47b447 Michael Hanselmann
103 6396dc04 Michael Hanselmann
# Feature strings
104 6396dc04 Michael Hanselmann
INST_CREATE_REQV1 = "instance-create-reqv1"
105 6396dc04 Michael Hanselmann
INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
106 6396dc04 Michael Hanselmann
NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
107 6396dc04 Michael Hanselmann
NODE_EVAC_RES1 = "node-evac-res1"
108 6396dc04 Michael Hanselmann
109 6396dc04 Michael Hanselmann
# Old feature constant names in case they're references by users of this module
110 6396dc04 Michael Hanselmann
_INST_CREATE_REQV1 = INST_CREATE_REQV1
111 6396dc04 Michael Hanselmann
_INST_REINSTALL_REQV1 = INST_REINSTALL_REQV1
112 6396dc04 Michael Hanselmann
_NODE_MIGRATE_REQV1 = NODE_MIGRATE_REQV1
113 6396dc04 Michael Hanselmann
_NODE_EVAC_RES1 = NODE_EVAC_RES1
114 6396dc04 Michael Hanselmann
115 2a7c3583 Michael Hanselmann
# Older pycURL versions don't have all error constants
116 2a7c3583 Michael Hanselmann
try:
117 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
118 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
119 2a7c3583 Michael Hanselmann
except AttributeError:
120 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = 60
121 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = 77
122 2a7c3583 Michael Hanselmann
123 2a7c3583 Michael Hanselmann
_CURL_SSL_CERT_ERRORS = frozenset([
124 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT,
125 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE,
126 2a7c3583 Michael Hanselmann
  ])
127 2a7c3583 Michael Hanselmann
128 95ab4de9 David Knowles
129 95ab4de9 David Knowles
class Error(Exception):
130 95ab4de9 David Knowles
  """Base error class for this module.
131 95ab4de9 David Knowles

132 95ab4de9 David Knowles
  """
133 95ab4de9 David Knowles
  pass
134 95ab4de9 David Knowles
135 95ab4de9 David Knowles
136 95ab4de9 David Knowles
class GanetiApiError(Error):
137 95ab4de9 David Knowles
  """Generic error raised from Ganeti API.
138 95ab4de9 David Knowles

139 95ab4de9 David Knowles
  """
140 8a019a03 Michael Hanselmann
  def __init__(self, msg, code=None):
141 8a019a03 Michael Hanselmann
    Error.__init__(self, msg)
142 8a019a03 Michael Hanselmann
    self.code = code
143 95ab4de9 David Knowles
144 95ab4de9 David Knowles
145 4b129313 Chris Schrier
class CertificateError(GanetiApiError):
146 4b129313 Chris Schrier
  """Raised when a problem is found with the SSL certificate.
147 4b129313 Chris Schrier

148 4b129313 Chris Schrier
  """
149 4b129313 Chris Schrier
  pass
150 4b129313 Chris Schrier
151 4b129313 Chris Schrier
152 4c864b55 Michael Hanselmann
def _AppendIf(container, condition, value):
153 4c864b55 Michael Hanselmann
  """Appends to a list if a condition evaluates to truth.
154 4c864b55 Michael Hanselmann

155 4c864b55 Michael Hanselmann
  """
156 4c864b55 Michael Hanselmann
  if condition:
157 4c864b55 Michael Hanselmann
    container.append(value)
158 4c864b55 Michael Hanselmann
159 4c864b55 Michael Hanselmann
  return condition
160 4c864b55 Michael Hanselmann
161 4c864b55 Michael Hanselmann
162 4c864b55 Michael Hanselmann
def _AppendDryRunIf(container, condition):
163 4c864b55 Michael Hanselmann
  """Appends a "dry-run" parameter if a condition evaluates to truth.
164 4c864b55 Michael Hanselmann

165 4c864b55 Michael Hanselmann
  """
166 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_DRY_RUN, 1))
167 4c864b55 Michael Hanselmann
168 4c864b55 Michael Hanselmann
169 4c864b55 Michael Hanselmann
def _AppendForceIf(container, condition):
170 4c864b55 Michael Hanselmann
  """Appends a "force" parameter if a condition evaluates to truth.
171 4c864b55 Michael Hanselmann

172 4c864b55 Michael Hanselmann
  """
173 4c864b55 Michael Hanselmann
  return _AppendIf(container, condition, (_QPARAM_FORCE, 1))
174 4c864b55 Michael Hanselmann
175 4c864b55 Michael Hanselmann
176 57d8e228 Michael Hanselmann
def _SetItemIf(container, condition, item, value):
177 57d8e228 Michael Hanselmann
  """Sets an item if a condition evaluates to truth.
178 57d8e228 Michael Hanselmann

179 57d8e228 Michael Hanselmann
  """
180 57d8e228 Michael Hanselmann
  if condition:
181 57d8e228 Michael Hanselmann
    container[item] = value
182 57d8e228 Michael Hanselmann
183 57d8e228 Michael Hanselmann
  return condition
184 57d8e228 Michael Hanselmann
185 57d8e228 Michael Hanselmann
186 2a7c3583 Michael Hanselmann
def UsesRapiClient(fn):
187 2a7c3583 Michael Hanselmann
  """Decorator for code using RAPI client to initialize pycURL.
188 9279e986 Michael Hanselmann

189 9279e986 Michael Hanselmann
  """
190 2a7c3583 Michael Hanselmann
  def wrapper(*args, **kwargs):
191 2a7c3583 Michael Hanselmann
    # curl_global_init(3) and curl_global_cleanup(3) must be called with only
192 2a7c3583 Michael Hanselmann
    # one thread running. This check is just a safety measure -- it doesn't
193 2a7c3583 Michael Hanselmann
    # cover all cases.
194 2a7c3583 Michael Hanselmann
    assert threading.activeCount() == 1, \
195 2a7c3583 Michael Hanselmann
           "Found active threads when initializing pycURL"
196 2a7c3583 Michael Hanselmann
197 2a7c3583 Michael Hanselmann
    pycurl.global_init(pycurl.GLOBAL_ALL)
198 2a7c3583 Michael Hanselmann
    try:
199 2a7c3583 Michael Hanselmann
      return fn(*args, **kwargs)
200 2a7c3583 Michael Hanselmann
    finally:
201 2a7c3583 Michael Hanselmann
      pycurl.global_cleanup()
202 2a7c3583 Michael Hanselmann
203 2a7c3583 Michael Hanselmann
  return wrapper
204 2a7c3583 Michael Hanselmann
205 2a7c3583 Michael Hanselmann
206 2a7c3583 Michael Hanselmann
def GenericCurlConfig(verbose=False, use_signal=False,
207 2a7c3583 Michael Hanselmann
                      use_curl_cabundle=False, cafile=None, capath=None,
208 2a7c3583 Michael Hanselmann
                      proxy=None, verify_hostname=False,
209 2a7c3583 Michael Hanselmann
                      connect_timeout=None, timeout=None,
210 2a7c3583 Michael Hanselmann
                      _pycurl_version_fn=pycurl.version_info):
211 2a7c3583 Michael Hanselmann
  """Curl configuration function generator.
212 2a7c3583 Michael Hanselmann

213 2a7c3583 Michael Hanselmann
  @type verbose: bool
214 2a7c3583 Michael Hanselmann
  @param verbose: Whether to set cURL to verbose mode
215 2a7c3583 Michael Hanselmann
  @type use_signal: bool
216 2a7c3583 Michael Hanselmann
  @param use_signal: Whether to allow cURL to use signals
217 2a7c3583 Michael Hanselmann
  @type use_curl_cabundle: bool
218 2a7c3583 Michael Hanselmann
  @param use_curl_cabundle: Whether to use cURL's default CA bundle
219 2a7c3583 Michael Hanselmann
  @type cafile: string
220 2a7c3583 Michael Hanselmann
  @param cafile: In which file we can find the certificates
221 2a7c3583 Michael Hanselmann
  @type capath: string
222 2a7c3583 Michael Hanselmann
  @param capath: In which directory we can find the certificates
223 2a7c3583 Michael Hanselmann
  @type proxy: string
224 2a7c3583 Michael Hanselmann
  @param proxy: Proxy to use, None for default behaviour and empty string for
225 2a7c3583 Michael Hanselmann
                disabling proxies (see curl_easy_setopt(3))
226 2a7c3583 Michael Hanselmann
  @type verify_hostname: bool
227 2a7c3583 Michael Hanselmann
  @param verify_hostname: Whether to verify the remote peer certificate's
228 2a7c3583 Michael Hanselmann
                          commonName
229 2a7c3583 Michael Hanselmann
  @type connect_timeout: number
230 2a7c3583 Michael Hanselmann
  @param connect_timeout: Timeout for establishing connection in seconds
231 2a7c3583 Michael Hanselmann
  @type timeout: number
232 2a7c3583 Michael Hanselmann
  @param timeout: Timeout for complete transfer in seconds (see
233 2a7c3583 Michael Hanselmann
                  curl_easy_setopt(3)).
234 9279e986 Michael Hanselmann

235 9279e986 Michael Hanselmann
  """
236 2a7c3583 Michael Hanselmann
  if use_curl_cabundle and (cafile or capath):
237 2a7c3583 Michael Hanselmann
    raise Error("Can not use default CA bundle when CA file or path is set")
238 9279e986 Michael Hanselmann
239 2a7c3583 Michael Hanselmann
  def _ConfigCurl(curl, logger):
240 2a7c3583 Michael Hanselmann
    """Configures a cURL object
241 9279e986 Michael Hanselmann

242 2a7c3583 Michael Hanselmann
    @type curl: pycurl.Curl
243 2a7c3583 Michael Hanselmann
    @param curl: cURL object
244 9279e986 Michael Hanselmann

245 9279e986 Michael Hanselmann
    """
246 2a7c3583 Michael Hanselmann
    logger.debug("Using cURL version %s", pycurl.version)
247 2a7c3583 Michael Hanselmann
248 2a7c3583 Michael Hanselmann
    # pycurl.version_info returns a tuple with information about the used
249 2a7c3583 Michael Hanselmann
    # version of libcurl. Item 5 is the SSL library linked to it.
250 2a7c3583 Michael Hanselmann
    # e.g.: (3, '7.18.0', 463360, 'x86_64-pc-linux-gnu', 1581, 'GnuTLS/2.0.4',
251 2a7c3583 Michael Hanselmann
    # 0, '1.2.3.3', ...)
252 2a7c3583 Michael Hanselmann
    sslver = _pycurl_version_fn()[5]
253 2a7c3583 Michael Hanselmann
    if not sslver:
254 2a7c3583 Michael Hanselmann
      raise Error("No SSL support in cURL")
255 2a7c3583 Michael Hanselmann
256 2a7c3583 Michael Hanselmann
    lcsslver = sslver.lower()
257 2a7c3583 Michael Hanselmann
    if lcsslver.startswith("openssl/"):
258 2a7c3583 Michael Hanselmann
      pass
259 592eb0df Renรฉ Nussbaumer
    elif lcsslver.startswith("nss/"):
260 592eb0df Renรฉ Nussbaumer
      # TODO: investigate compatibility beyond a simple test
261 592eb0df Renรฉ Nussbaumer
      pass
262 2a7c3583 Michael Hanselmann
    elif lcsslver.startswith("gnutls/"):
263 2a7c3583 Michael Hanselmann
      if capath:
264 2a7c3583 Michael Hanselmann
        raise Error("cURL linked against GnuTLS has no support for a"
265 2a7c3583 Michael Hanselmann
                    " CA path (%s)" % (pycurl.version, ))
266 9279e986 Michael Hanselmann
    else:
267 2a7c3583 Michael Hanselmann
      raise NotImplementedError("cURL uses unsupported SSL version '%s'" %
268 2a7c3583 Michael Hanselmann
                                sslver)
269 2a7c3583 Michael Hanselmann
270 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, verbose)
271 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, not use_signal)
272 2a7c3583 Michael Hanselmann
273 2a7c3583 Michael Hanselmann
    # Whether to verify remote peer's CN
274 2a7c3583 Michael Hanselmann
    if verify_hostname:
275 2a7c3583 Michael Hanselmann
      # curl_easy_setopt(3): "When CURLOPT_SSL_VERIFYHOST is 2, that
276 2a7c3583 Michael Hanselmann
      # certificate must indicate that the server is the server to which you
277 2a7c3583 Michael Hanselmann
      # meant to connect, or the connection fails. [...] When the value is 1,
278 2a7c3583 Michael Hanselmann
      # the certificate must contain a Common Name field, but it doesn't matter
279 2a7c3583 Michael Hanselmann
      # what name it says. [...]"
280 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 2)
281 beba56ae Michael Hanselmann
    else:
282 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYHOST, 0)
283 2a7c3583 Michael Hanselmann
284 2a7c3583 Michael Hanselmann
    if cafile or capath or use_curl_cabundle:
285 2a7c3583 Michael Hanselmann
      # Require certificates to be checked
286 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, True)
287 2a7c3583 Michael Hanselmann
      if cafile:
288 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAINFO, str(cafile))
289 2a7c3583 Michael Hanselmann
      if capath:
290 2a7c3583 Michael Hanselmann
        curl.setopt(pycurl.CAPATH, str(capath))
291 2a7c3583 Michael Hanselmann
      # Not changing anything for using default CA bundle
292 2a7c3583 Michael Hanselmann
    else:
293 2a7c3583 Michael Hanselmann
      # Disable SSL certificate verification
294 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.SSL_VERIFYPEER, False)
295 9279e986 Michael Hanselmann
296 2a7c3583 Michael Hanselmann
    if proxy is not None:
297 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.PROXY, str(proxy))
298 9279e986 Michael Hanselmann
299 2a7c3583 Michael Hanselmann
    # Timeouts
300 2a7c3583 Michael Hanselmann
    if connect_timeout is not None:
301 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.CONNECTTIMEOUT, connect_timeout)
302 2a7c3583 Michael Hanselmann
    if timeout is not None:
303 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.TIMEOUT, timeout)
304 9279e986 Michael Hanselmann
305 2a7c3583 Michael Hanselmann
  return _ConfigCurl
306 9279e986 Michael Hanselmann
307 9279e986 Michael Hanselmann
308 b459a848 Andrea Spadaccini
class GanetiRapiClient(object): # pylint: disable=R0904
309 95ab4de9 David Knowles
  """Ganeti RAPI client.
310 95ab4de9 David Knowles

311 95ab4de9 David Knowles
  """
312 95ab4de9 David Knowles
  USER_AGENT = "Ganeti RAPI Client"
313 d3844674 Michael Hanselmann
  _json_encoder = simplejson.JSONEncoder(sort_keys=True)
314 95ab4de9 David Knowles
315 9279e986 Michael Hanselmann
  def __init__(self, host, port=GANETI_RAPI_PORT,
316 2a7c3583 Michael Hanselmann
               username=None, password=None, logger=logging,
317 a5eba783 Michael Hanselmann
               curl_config_fn=None, curl_factory=None):
318 2a7c3583 Michael Hanselmann
    """Initializes this class.
319 95ab4de9 David Knowles

320 9279e986 Michael Hanselmann
    @type host: string
321 9279e986 Michael Hanselmann
    @param host: the ganeti cluster master to interact with
322 95ab4de9 David Knowles
    @type port: int
323 9279e986 Michael Hanselmann
    @param port: the port on which the RAPI is running (default is 5080)
324 9279e986 Michael Hanselmann
    @type username: string
325 95ab4de9 David Knowles
    @param username: the username to connect with
326 9279e986 Michael Hanselmann
    @type password: string
327 95ab4de9 David Knowles
    @param password: the password to connect with
328 2a7c3583 Michael Hanselmann
    @type curl_config_fn: callable
329 2a7c3583 Michael Hanselmann
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
330 9279e986 Michael Hanselmann
    @param logger: Logging object
331 95ab4de9 David Knowles

332 95ab4de9 David Knowles
    """
333 a5eba783 Michael Hanselmann
    self._username = username
334 a5eba783 Michael Hanselmann
    self._password = password
335 9279e986 Michael Hanselmann
    self._logger = logger
336 a5eba783 Michael Hanselmann
    self._curl_config_fn = curl_config_fn
337 a5eba783 Michael Hanselmann
    self._curl_factory = curl_factory
338 95ab4de9 David Knowles
339 1a8337f2 Manuel Franceschini
    try:
340 1a8337f2 Manuel Franceschini
      socket.inet_pton(socket.AF_INET6, host)
341 1a8337f2 Manuel Franceschini
      address = "[%s]:%s" % (host, port)
342 1a8337f2 Manuel Franceschini
    except socket.error:
343 1a8337f2 Manuel Franceschini
      address = "%s:%s" % (host, port)
344 1a8337f2 Manuel Franceschini
345 1a8337f2 Manuel Franceschini
    self._base_url = "https://%s" % address
346 f2f88abf David Knowles
347 a5eba783 Michael Hanselmann
    if username is not None:
348 a5eba783 Michael Hanselmann
      if password is None:
349 a5eba783 Michael Hanselmann
        raise Error("Password not specified")
350 a5eba783 Michael Hanselmann
    elif password:
351 a5eba783 Michael Hanselmann
      raise Error("Specified password without username")
352 a5eba783 Michael Hanselmann
353 a5eba783 Michael Hanselmann
  def _CreateCurl(self):
354 a5eba783 Michael Hanselmann
    """Creates a cURL object.
355 a5eba783 Michael Hanselmann

356 a5eba783 Michael Hanselmann
    """
357 a5eba783 Michael Hanselmann
    # Create pycURL object if no factory is provided
358 a5eba783 Michael Hanselmann
    if self._curl_factory:
359 a5eba783 Michael Hanselmann
      curl = self._curl_factory()
360 a5eba783 Michael Hanselmann
    else:
361 2a7c3583 Michael Hanselmann
      curl = pycurl.Curl()
362 2a7c3583 Michael Hanselmann
363 2a7c3583 Michael Hanselmann
    # Default cURL settings
364 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.VERBOSE, False)
365 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.FOLLOWLOCATION, False)
366 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.MAXREDIRS, 5)
367 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.NOSIGNAL, True)
368 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.USERAGENT, self.USER_AGENT)
369 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYHOST, 0)
370 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.SSL_VERIFYPEER, False)
371 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.HTTPHEADER, [
372 2a7c3583 Michael Hanselmann
      "Accept: %s" % HTTP_APP_JSON,
373 2a7c3583 Michael Hanselmann
      "Content-type: %s" % HTTP_APP_JSON,
374 2a7c3583 Michael Hanselmann
      ])
375 2a7c3583 Michael Hanselmann
376 a5eba783 Michael Hanselmann
    assert ((self._username is None and self._password is None) ^
377 a5eba783 Michael Hanselmann
            (self._username is not None and self._password is not None))
378 a5eba783 Michael Hanselmann
379 a5eba783 Michael Hanselmann
    if self._username:
380 a5eba783 Michael Hanselmann
      # Setup authentication
381 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
382 a5eba783 Michael Hanselmann
      curl.setopt(pycurl.USERPWD,
383 a5eba783 Michael Hanselmann
                  str("%s:%s" % (self._username, self._password)))
384 9279e986 Michael Hanselmann
385 2a7c3583 Michael Hanselmann
    # Call external configuration function
386 a5eba783 Michael Hanselmann
    if self._curl_config_fn:
387 a5eba783 Michael Hanselmann
      self._curl_config_fn(curl, self._logger)
388 f2f88abf David Knowles
389 a5eba783 Michael Hanselmann
    return curl
390 95ab4de9 David Knowles
391 10f5ab6c Michael Hanselmann
  @staticmethod
392 10f5ab6c Michael Hanselmann
  def _EncodeQuery(query):
393 10f5ab6c Michael Hanselmann
    """Encode query values for RAPI URL.
394 10f5ab6c Michael Hanselmann

395 10f5ab6c Michael Hanselmann
    @type query: list of two-tuples
396 10f5ab6c Michael Hanselmann
    @param query: Query arguments
397 10f5ab6c Michael Hanselmann
    @rtype: list
398 10f5ab6c Michael Hanselmann
    @return: Query list with encoded values
399 10f5ab6c Michael Hanselmann

400 10f5ab6c Michael Hanselmann
    """
401 10f5ab6c Michael Hanselmann
    result = []
402 10f5ab6c Michael Hanselmann
403 10f5ab6c Michael Hanselmann
    for name, value in query:
404 10f5ab6c Michael Hanselmann
      if value is None:
405 10f5ab6c Michael Hanselmann
        result.append((name, ""))
406 10f5ab6c Michael Hanselmann
407 10f5ab6c Michael Hanselmann
      elif isinstance(value, bool):
408 10f5ab6c Michael Hanselmann
        # Boolean values must be encoded as 0 or 1
409 10f5ab6c Michael Hanselmann
        result.append((name, int(value)))
410 10f5ab6c Michael Hanselmann
411 10f5ab6c Michael Hanselmann
      elif isinstance(value, (list, tuple, dict)):
412 10f5ab6c Michael Hanselmann
        raise ValueError("Invalid query data type %r" % type(value).__name__)
413 10f5ab6c Michael Hanselmann
414 10f5ab6c Michael Hanselmann
      else:
415 10f5ab6c Michael Hanselmann
        result.append((name, value))
416 10f5ab6c Michael Hanselmann
417 10f5ab6c Michael Hanselmann
    return result
418 10f5ab6c Michael Hanselmann
419 768747ed Michael Hanselmann
  def _SendRequest(self, method, path, query, content):
420 95ab4de9 David Knowles
    """Sends an HTTP request.
421 95ab4de9 David Knowles

422 95ab4de9 David Knowles
    This constructs a full URL, encodes and decodes HTTP bodies, and
423 95ab4de9 David Knowles
    handles invalid responses in a pythonic way.
424 95ab4de9 David Knowles

425 768747ed Michael Hanselmann
    @type method: string
426 95ab4de9 David Knowles
    @param method: HTTP method to use
427 768747ed Michael Hanselmann
    @type path: string
428 95ab4de9 David Knowles
    @param path: HTTP URL path
429 95ab4de9 David Knowles
    @type query: list of two-tuples
430 95ab4de9 David Knowles
    @param query: query arguments to pass to urllib.urlencode
431 95ab4de9 David Knowles
    @type content: str or None
432 95ab4de9 David Knowles
    @param content: HTTP body content
433 95ab4de9 David Knowles

434 95ab4de9 David Knowles
    @rtype: str
435 95ab4de9 David Knowles
    @return: JSON-Decoded response
436 95ab4de9 David Knowles

437 f2f88abf David Knowles
    @raises CertificateError: If an invalid SSL certificate is found
438 95ab4de9 David Knowles
    @raises GanetiApiError: If an invalid response is returned
439 95ab4de9 David Knowles

440 95ab4de9 David Knowles
    """
441 ccd6b542 Michael Hanselmann
    assert path.startswith("/")
442 ccd6b542 Michael Hanselmann
443 a5eba783 Michael Hanselmann
    curl = self._CreateCurl()
444 2a7c3583 Michael Hanselmann
445 8306e0e4 Michael Hanselmann
    if content is not None:
446 d3844674 Michael Hanselmann
      encoded_content = self._json_encoder.encode(content)
447 d3844674 Michael Hanselmann
    else:
448 2a7c3583 Michael Hanselmann
      encoded_content = ""
449 95ab4de9 David Knowles
450 ccd6b542 Michael Hanselmann
    # Build URL
451 f961e2ba Michael Hanselmann
    urlparts = [self._base_url, path]
452 ccd6b542 Michael Hanselmann
    if query:
453 f961e2ba Michael Hanselmann
      urlparts.append("?")
454 f961e2ba Michael Hanselmann
      urlparts.append(urllib.urlencode(self._EncodeQuery(query)))
455 9279e986 Michael Hanselmann
456 f961e2ba Michael Hanselmann
    url = "".join(urlparts)
457 f961e2ba Michael Hanselmann
458 a5eba783 Michael Hanselmann
    self._logger.debug("Sending request %s %s (content=%r)",
459 a5eba783 Michael Hanselmann
                       method, url, encoded_content)
460 2a7c3583 Michael Hanselmann
461 2a7c3583 Michael Hanselmann
    # Buffer for response
462 2a7c3583 Michael Hanselmann
    encoded_resp_body = StringIO()
463 f961e2ba Michael Hanselmann
464 2a7c3583 Michael Hanselmann
    # Configure cURL
465 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.CUSTOMREQUEST, str(method))
466 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.URL, str(url))
467 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.POSTFIELDS, str(encoded_content))
468 2a7c3583 Michael Hanselmann
    curl.setopt(pycurl.WRITEFUNCTION, encoded_resp_body.write)
469 9279e986 Michael Hanselmann
470 f2f88abf David Knowles
    try:
471 2a7c3583 Michael Hanselmann
      # Send request and wait for response
472 2a7c3583 Michael Hanselmann
      try:
473 2a7c3583 Michael Hanselmann
        curl.perform()
474 2a7c3583 Michael Hanselmann
      except pycurl.error, err:
475 2a7c3583 Michael Hanselmann
        if err.args[0] in _CURL_SSL_CERT_ERRORS:
476 4b129313 Chris Schrier
          raise CertificateError("SSL certificate error %s" % err,
477 4b129313 Chris Schrier
                                 code=err.args[0])
478 2a7c3583 Michael Hanselmann
479 b5800ee9 Chris Schrier
        raise GanetiApiError(str(err), code=err.args[0])
480 2a7c3583 Michael Hanselmann
    finally:
481 2a7c3583 Michael Hanselmann
      # Reset settings to not keep references to large objects in memory
482 2a7c3583 Michael Hanselmann
      # between requests
483 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.POSTFIELDS, "")
484 2a7c3583 Michael Hanselmann
      curl.setopt(pycurl.WRITEFUNCTION, lambda _: None)
485 2a7c3583 Michael Hanselmann
486 2a7c3583 Michael Hanselmann
    # Get HTTP response code
487 2a7c3583 Michael Hanselmann
    http_code = curl.getinfo(pycurl.RESPONSE_CODE)
488 2a7c3583 Michael Hanselmann
489 2a7c3583 Michael Hanselmann
    # Was anything written to the response buffer?
490 2a7c3583 Michael Hanselmann
    if encoded_resp_body.tell():
491 2a7c3583 Michael Hanselmann
      response_content = simplejson.loads(encoded_resp_body.getvalue())
492 d3844674 Michael Hanselmann
    else:
493 d3844674 Michael Hanselmann
      response_content = None
494 95ab4de9 David Knowles
495 2a7c3583 Michael Hanselmann
    if http_code != HTTP_OK:
496 d3844674 Michael Hanselmann
      if isinstance(response_content, dict):
497 95ab4de9 David Knowles
        msg = ("%s %s: %s" %
498 d3844674 Michael Hanselmann
               (response_content["code"],
499 d3844674 Michael Hanselmann
                response_content["message"],
500 d3844674 Michael Hanselmann
                response_content["explain"]))
501 95ab4de9 David Knowles
      else:
502 d3844674 Michael Hanselmann
        msg = str(response_content)
503 d3844674 Michael Hanselmann
504 2a7c3583 Michael Hanselmann
      raise GanetiApiError(msg, code=http_code)
505 95ab4de9 David Knowles
506 d3844674 Michael Hanselmann
    return response_content
507 95ab4de9 David Knowles
508 95ab4de9 David Knowles
  def GetVersion(self):
509 cab667cc David Knowles
    """Gets the Remote API version running on the cluster.
510 95ab4de9 David Knowles

511 95ab4de9 David Knowles
    @rtype: int
512 f2f88abf David Knowles
    @return: Ganeti Remote API version
513 95ab4de9 David Knowles

514 95ab4de9 David Knowles
    """
515 768747ed Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/version", None, None)
516 95ab4de9 David Knowles
517 7eac4a4d Michael Hanselmann
  def GetFeatures(self):
518 7eac4a4d Michael Hanselmann
    """Gets the list of optional features supported by RAPI server.
519 7eac4a4d Michael Hanselmann

520 7eac4a4d Michael Hanselmann
    @rtype: list
521 7eac4a4d Michael Hanselmann
    @return: List of optional features
522 7eac4a4d Michael Hanselmann

523 7eac4a4d Michael Hanselmann
    """
524 7eac4a4d Michael Hanselmann
    try:
525 7eac4a4d Michael Hanselmann
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
526 7eac4a4d Michael Hanselmann
                               None, None)
527 7eac4a4d Michael Hanselmann
    except GanetiApiError, err:
528 7eac4a4d Michael Hanselmann
      # Older RAPI servers don't support this resource
529 7eac4a4d Michael Hanselmann
      if err.code == HTTP_NOT_FOUND:
530 7eac4a4d Michael Hanselmann
        return []
531 7eac4a4d Michael Hanselmann
532 7eac4a4d Michael Hanselmann
      raise
533 7eac4a4d Michael Hanselmann
534 95ab4de9 David Knowles
  def GetOperatingSystems(self):
535 95ab4de9 David Knowles
    """Gets the Operating Systems running in the Ganeti cluster.
536 95ab4de9 David Knowles

537 95ab4de9 David Knowles
    @rtype: list of str
538 95ab4de9 David Knowles
    @return: operating systems
539 95ab4de9 David Knowles

540 95ab4de9 David Knowles
    """
541 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
542 a198b2d9 Michael Hanselmann
                             None, None)
543 95ab4de9 David Knowles
544 95ab4de9 David Knowles
  def GetInfo(self):
545 95ab4de9 David Knowles
    """Gets info about the cluster.
546 95ab4de9 David Knowles

547 95ab4de9 David Knowles
    @rtype: dict
548 95ab4de9 David Knowles
    @return: information about the cluster
549 95ab4de9 David Knowles

550 95ab4de9 David Knowles
    """
551 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
552 a198b2d9 Michael Hanselmann
                             None, None)
553 95ab4de9 David Knowles
554 54d4c13b Michael Hanselmann
  def RedistributeConfig(self):
555 54d4c13b Michael Hanselmann
    """Tells the cluster to redistribute its configuration files.
556 54d4c13b Michael Hanselmann

557 d914c76f Simeon Miteff
    @rtype: string
558 54d4c13b Michael Hanselmann
    @return: job id
559 54d4c13b Michael Hanselmann

560 54d4c13b Michael Hanselmann
    """
561 54d4c13b Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
562 54d4c13b Michael Hanselmann
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
563 54d4c13b Michael Hanselmann
                             None, None)
564 54d4c13b Michael Hanselmann
565 62e999a5 Michael Hanselmann
  def ModifyCluster(self, **kwargs):
566 62e999a5 Michael Hanselmann
    """Modifies cluster parameters.
567 62e999a5 Michael Hanselmann

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

570 98805538 Michael Hanselmann
    @rtype: string
571 62e999a5 Michael Hanselmann
    @return: job id
572 62e999a5 Michael Hanselmann

573 62e999a5 Michael Hanselmann
    """
574 62e999a5 Michael Hanselmann
    body = kwargs
575 62e999a5 Michael Hanselmann
576 62e999a5 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
577 62e999a5 Michael Hanselmann
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
578 62e999a5 Michael Hanselmann
579 95ab4de9 David Knowles
  def GetClusterTags(self):
580 95ab4de9 David Knowles
    """Gets the cluster tags.
581 95ab4de9 David Knowles

582 95ab4de9 David Knowles
    @rtype: list of str
583 95ab4de9 David Knowles
    @return: cluster tags
584 95ab4de9 David Knowles

585 95ab4de9 David Knowles
    """
586 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
587 a198b2d9 Michael Hanselmann
                             None, None)
588 95ab4de9 David Knowles
589 95ab4de9 David Knowles
  def AddClusterTags(self, tags, dry_run=False):
590 95ab4de9 David Knowles
    """Adds tags to the cluster.
591 95ab4de9 David Knowles

592 95ab4de9 David Knowles
    @type tags: list of str
593 95ab4de9 David Knowles
    @param tags: tags to add to the cluster
594 95ab4de9 David Knowles
    @type dry_run: bool
595 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
596 95ab4de9 David Knowles

597 98805538 Michael Hanselmann
    @rtype: string
598 95ab4de9 David Knowles
    @return: job id
599 95ab4de9 David Knowles

600 95ab4de9 David Knowles
    """
601 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
602 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
603 95ab4de9 David Knowles
604 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
605 a198b2d9 Michael Hanselmann
                             query, None)
606 95ab4de9 David Knowles
607 95ab4de9 David Knowles
  def DeleteClusterTags(self, tags, dry_run=False):
608 95ab4de9 David Knowles
    """Deletes tags from the cluster.
609 95ab4de9 David Knowles

610 95ab4de9 David Knowles
    @type tags: list of str
611 95ab4de9 David Knowles
    @param tags: tags to delete
612 95ab4de9 David Knowles
    @type dry_run: bool
613 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
614 d914c76f Simeon Miteff
    @rtype: string
615 d914c76f Simeon Miteff
    @return: job id
616 95ab4de9 David Knowles

617 95ab4de9 David Knowles
    """
618 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
619 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
620 95ab4de9 David Knowles
621 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
622 a198b2d9 Michael Hanselmann
                             query, None)
623 95ab4de9 David Knowles
624 95ab4de9 David Knowles
  def GetInstances(self, bulk=False):
625 95ab4de9 David Knowles
    """Gets information about instances on the cluster.
626 95ab4de9 David Knowles

627 95ab4de9 David Knowles
    @type bulk: bool
628 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
629 95ab4de9 David Knowles

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

633 95ab4de9 David Knowles
    """
634 95ab4de9 David Knowles
    query = []
635 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
636 95ab4de9 David Knowles
637 a198b2d9 Michael Hanselmann
    instances = self._SendRequest(HTTP_GET,
638 a198b2d9 Michael Hanselmann
                                  "/%s/instances" % GANETI_RAPI_VERSION,
639 a198b2d9 Michael Hanselmann
                                  query, None)
640 95ab4de9 David Knowles
    if bulk:
641 95ab4de9 David Knowles
      return instances
642 95ab4de9 David Knowles
    else:
643 95ab4de9 David Knowles
      return [i["id"] for i in instances]
644 95ab4de9 David Knowles
645 591e5103 Michael Hanselmann
  def GetInstance(self, instance):
646 95ab4de9 David Knowles
    """Gets information about an instance.
647 95ab4de9 David Knowles

648 95ab4de9 David Knowles
    @type instance: str
649 95ab4de9 David Knowles
    @param instance: instance whose info to return
650 95ab4de9 David Knowles

651 95ab4de9 David Knowles
    @rtype: dict
652 95ab4de9 David Knowles
    @return: info about the instance
653 95ab4de9 David Knowles

654 95ab4de9 David Knowles
    """
655 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
656 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
657 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
658 95ab4de9 David Knowles
659 591e5103 Michael Hanselmann
  def GetInstanceInfo(self, instance, static=None):
660 591e5103 Michael Hanselmann
    """Gets information about an instance.
661 591e5103 Michael Hanselmann

662 591e5103 Michael Hanselmann
    @type instance: string
663 591e5103 Michael Hanselmann
    @param instance: Instance name
664 591e5103 Michael Hanselmann
    @rtype: string
665 591e5103 Michael Hanselmann
    @return: Job ID
666 591e5103 Michael Hanselmann

667 591e5103 Michael Hanselmann
    """
668 591e5103 Michael Hanselmann
    if static is not None:
669 591e5103 Michael Hanselmann
      query = [("static", static)]
670 591e5103 Michael Hanselmann
    else:
671 591e5103 Michael Hanselmann
      query = None
672 591e5103 Michael Hanselmann
673 591e5103 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
674 591e5103 Michael Hanselmann
                             ("/%s/instances/%s/info" %
675 591e5103 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
676 591e5103 Michael Hanselmann
677 8a47b447 Michael Hanselmann
  def CreateInstance(self, mode, name, disk_template, disks, nics,
678 8a47b447 Michael Hanselmann
                     **kwargs):
679 95ab4de9 David Knowles
    """Creates a new instance.
680 95ab4de9 David Knowles

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

683 8a47b447 Michael Hanselmann
    @type mode: string
684 8a47b447 Michael Hanselmann
    @param mode: Instance creation mode
685 8a47b447 Michael Hanselmann
    @type name: string
686 8a47b447 Michael Hanselmann
    @param name: Hostname of the instance to create
687 8a47b447 Michael Hanselmann
    @type disk_template: string
688 8a47b447 Michael Hanselmann
    @param disk_template: Disk template for instance (e.g. plain, diskless,
689 8a47b447 Michael Hanselmann
                          file, or drbd)
690 8a47b447 Michael Hanselmann
    @type disks: list of dicts
691 8a47b447 Michael Hanselmann
    @param disks: List of disk definitions
692 8a47b447 Michael Hanselmann
    @type nics: list of dicts
693 8a47b447 Michael Hanselmann
    @param nics: List of NIC definitions
694 95ab4de9 David Knowles
    @type dry_run: bool
695 8a47b447 Michael Hanselmann
    @keyword dry_run: whether to perform a dry run
696 95ab4de9 David Knowles

697 98805538 Michael Hanselmann
    @rtype: string
698 95ab4de9 David Knowles
    @return: job id
699 95ab4de9 David Knowles

700 95ab4de9 David Knowles
    """
701 95ab4de9 David Knowles
    query = []
702 8a47b447 Michael Hanselmann
703 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, kwargs.get("dry_run"))
704 95ab4de9 David Knowles
705 8a47b447 Michael Hanselmann
    if _INST_CREATE_REQV1 in self.GetFeatures():
706 8a47b447 Michael Hanselmann
      # All required fields for request data version 1
707 8a47b447 Michael Hanselmann
      body = {
708 8a47b447 Michael Hanselmann
        _REQ_DATA_VERSION_FIELD: 1,
709 8a47b447 Michael Hanselmann
        "mode": mode,
710 8a47b447 Michael Hanselmann
        "name": name,
711 8a47b447 Michael Hanselmann
        "disk_template": disk_template,
712 8a47b447 Michael Hanselmann
        "disks": disks,
713 8a47b447 Michael Hanselmann
        "nics": nics,
714 8a47b447 Michael Hanselmann
        }
715 8a47b447 Michael Hanselmann
716 8a47b447 Michael Hanselmann
      conflicts = set(kwargs.iterkeys()) & set(body.iterkeys())
717 8a47b447 Michael Hanselmann
      if conflicts:
718 8a47b447 Michael Hanselmann
        raise GanetiApiError("Required fields can not be specified as"
719 8a47b447 Michael Hanselmann
                             " keywords: %s" % ", ".join(conflicts))
720 8a47b447 Michael Hanselmann
721 8a47b447 Michael Hanselmann
      body.update((key, value) for key, value in kwargs.iteritems()
722 8a47b447 Michael Hanselmann
                  if key != "dry_run")
723 8a47b447 Michael Hanselmann
    else:
724 9a8ae794 Michael Hanselmann
      raise GanetiApiError("Server does not support new-style (version 1)"
725 9a8ae794 Michael Hanselmann
                           " instance creation requests")
726 8a47b447 Michael Hanselmann
727 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
728 8a47b447 Michael Hanselmann
                             query, body)
729 95ab4de9 David Knowles
730 95ab4de9 David Knowles
  def DeleteInstance(self, instance, dry_run=False):
731 95ab4de9 David Knowles
    """Deletes an instance.
732 95ab4de9 David Knowles

733 95ab4de9 David Knowles
    @type instance: str
734 95ab4de9 David Knowles
    @param instance: the instance to delete
735 95ab4de9 David Knowles

736 98805538 Michael Hanselmann
    @rtype: string
737 cab667cc David Knowles
    @return: job id
738 cab667cc David Knowles

739 95ab4de9 David Knowles
    """
740 95ab4de9 David Knowles
    query = []
741 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
742 95ab4de9 David Knowles
743 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
744 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
745 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
746 95ab4de9 David Knowles
747 3b7158ef Michael Hanselmann
  def ModifyInstance(self, instance, **kwargs):
748 3b7158ef Michael Hanselmann
    """Modifies an instance.
749 3b7158ef Michael Hanselmann

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

752 3b7158ef Michael Hanselmann
    @type instance: string
753 3b7158ef Michael Hanselmann
    @param instance: Instance name
754 98805538 Michael Hanselmann
    @rtype: string
755 3b7158ef Michael Hanselmann
    @return: job id
756 3b7158ef Michael Hanselmann

757 3b7158ef Michael Hanselmann
    """
758 3b7158ef Michael Hanselmann
    body = kwargs
759 3b7158ef Michael Hanselmann
760 3b7158ef Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
761 3b7158ef Michael Hanselmann
                             ("/%s/instances/%s/modify" %
762 3b7158ef Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
763 3b7158ef Michael Hanselmann
764 b680c8be Michael Hanselmann
  def ActivateInstanceDisks(self, instance, ignore_size=None):
765 b680c8be Michael Hanselmann
    """Activates an instance's disks.
766 b680c8be Michael Hanselmann

767 b680c8be Michael Hanselmann
    @type instance: string
768 b680c8be Michael Hanselmann
    @param instance: Instance name
769 b680c8be Michael Hanselmann
    @type ignore_size: bool
770 b680c8be Michael Hanselmann
    @param ignore_size: Whether to ignore recorded size
771 d914c76f Simeon Miteff
    @rtype: string
772 b680c8be Michael Hanselmann
    @return: job id
773 b680c8be Michael Hanselmann

774 b680c8be Michael Hanselmann
    """
775 b680c8be Michael Hanselmann
    query = []
776 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_size, ("ignore_size", 1))
777 b680c8be Michael Hanselmann
778 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
779 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/activate-disks" %
780 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
781 b680c8be Michael Hanselmann
782 b680c8be Michael Hanselmann
  def DeactivateInstanceDisks(self, instance):
783 b680c8be Michael Hanselmann
    """Deactivates an instance's disks.
784 b680c8be Michael Hanselmann

785 b680c8be Michael Hanselmann
    @type instance: string
786 b680c8be Michael Hanselmann
    @param instance: Instance name
787 d914c76f Simeon Miteff
    @rtype: string
788 b680c8be Michael Hanselmann
    @return: job id
789 b680c8be Michael Hanselmann

790 b680c8be Michael Hanselmann
    """
791 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
792 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/deactivate-disks" %
793 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
794 b680c8be Michael Hanselmann
795 a52978c7 Michael Hanselmann
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
796 a52978c7 Michael Hanselmann
    """Recreate an instance's disks.
797 a52978c7 Michael Hanselmann

798 a52978c7 Michael Hanselmann
    @type instance: string
799 a52978c7 Michael Hanselmann
    @param instance: Instance name
800 a52978c7 Michael Hanselmann
    @type disks: list of int
801 a52978c7 Michael Hanselmann
    @param disks: List of disk indexes
802 a52978c7 Michael Hanselmann
    @type nodes: list of string
803 a52978c7 Michael Hanselmann
    @param nodes: New instance nodes, if relocation is desired
804 a52978c7 Michael Hanselmann
    @rtype: string
805 a52978c7 Michael Hanselmann
    @return: job id
806 a52978c7 Michael Hanselmann

807 a52978c7 Michael Hanselmann
    """
808 a52978c7 Michael Hanselmann
    body = {}
809 57d8e228 Michael Hanselmann
    _SetItemIf(body, disks is not None, "disks", disks)
810 57d8e228 Michael Hanselmann
    _SetItemIf(body, nodes is not None, "nodes", nodes)
811 a52978c7 Michael Hanselmann
812 a52978c7 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
813 a52978c7 Michael Hanselmann
                             ("/%s/instances/%s/recreate-disks" %
814 a52978c7 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
815 a52978c7 Michael Hanselmann
816 e23881ed Michael Hanselmann
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
817 e23881ed Michael Hanselmann
    """Grows a disk of an instance.
818 e23881ed Michael Hanselmann

819 e23881ed Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
820 e23881ed Michael Hanselmann

821 e23881ed Michael Hanselmann
    @type instance: string
822 e23881ed Michael Hanselmann
    @param instance: Instance name
823 e23881ed Michael Hanselmann
    @type disk: integer
824 e23881ed Michael Hanselmann
    @param disk: Disk index
825 e23881ed Michael Hanselmann
    @type amount: integer
826 e23881ed Michael Hanselmann
    @param amount: Grow disk by this amount (MiB)
827 e23881ed Michael Hanselmann
    @type wait_for_sync: bool
828 e23881ed Michael Hanselmann
    @param wait_for_sync: Wait for disk to synchronize
829 98805538 Michael Hanselmann
    @rtype: string
830 e23881ed Michael Hanselmann
    @return: job id
831 e23881ed Michael Hanselmann

832 e23881ed Michael Hanselmann
    """
833 e23881ed Michael Hanselmann
    body = {
834 e23881ed Michael Hanselmann
      "amount": amount,
835 e23881ed Michael Hanselmann
      }
836 e23881ed Michael Hanselmann
837 57d8e228 Michael Hanselmann
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
838 e23881ed Michael Hanselmann
839 e23881ed Michael Hanselmann
    return self._SendRequest(HTTP_POST,
840 e23881ed Michael Hanselmann
                             ("/%s/instances/%s/disk/%s/grow" %
841 e23881ed Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance, disk)),
842 e23881ed Michael Hanselmann
                             None, body)
843 e23881ed Michael Hanselmann
844 95ab4de9 David Knowles
  def GetInstanceTags(self, instance):
845 95ab4de9 David Knowles
    """Gets tags for an instance.
846 95ab4de9 David Knowles

847 95ab4de9 David Knowles
    @type instance: str
848 95ab4de9 David Knowles
    @param instance: instance whose tags to return
849 95ab4de9 David Knowles

850 95ab4de9 David Knowles
    @rtype: list of str
851 95ab4de9 David Knowles
    @return: tags for the instance
852 95ab4de9 David Knowles

853 95ab4de9 David Knowles
    """
854 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
855 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
856 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
857 95ab4de9 David Knowles
858 95ab4de9 David Knowles
  def AddInstanceTags(self, instance, tags, dry_run=False):
859 95ab4de9 David Knowles
    """Adds tags to an instance.
860 95ab4de9 David Knowles

861 95ab4de9 David Knowles
    @type instance: str
862 95ab4de9 David Knowles
    @param instance: instance to add tags to
863 95ab4de9 David Knowles
    @type tags: list of str
864 95ab4de9 David Knowles
    @param tags: tags to add to the instance
865 95ab4de9 David Knowles
    @type dry_run: bool
866 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
867 95ab4de9 David Knowles

868 98805538 Michael Hanselmann
    @rtype: string
869 95ab4de9 David Knowles
    @return: job id
870 95ab4de9 David Knowles

871 95ab4de9 David Knowles
    """
872 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
873 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
874 95ab4de9 David Knowles
875 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
876 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
877 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
878 95ab4de9 David Knowles
879 95ab4de9 David Knowles
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
880 95ab4de9 David Knowles
    """Deletes tags from an instance.
881 95ab4de9 David Knowles

882 95ab4de9 David Knowles
    @type instance: str
883 95ab4de9 David Knowles
    @param instance: instance to delete tags from
884 95ab4de9 David Knowles
    @type tags: list of str
885 95ab4de9 David Knowles
    @param tags: tags to delete
886 95ab4de9 David Knowles
    @type dry_run: bool
887 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
888 d914c76f Simeon Miteff
    @rtype: string
889 d914c76f Simeon Miteff
    @return: job id
890 95ab4de9 David Knowles

891 95ab4de9 David Knowles
    """
892 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
893 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
894 95ab4de9 David Knowles
895 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
896 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
897 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
898 95ab4de9 David Knowles
899 95ab4de9 David Knowles
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
900 95ab4de9 David Knowles
                     dry_run=False):
901 95ab4de9 David Knowles
    """Reboots an instance.
902 95ab4de9 David Knowles

903 95ab4de9 David Knowles
    @type instance: str
904 95ab4de9 David Knowles
    @param instance: instance to rebot
905 95ab4de9 David Knowles
    @type reboot_type: str
906 95ab4de9 David Knowles
    @param reboot_type: one of: hard, soft, full
907 95ab4de9 David Knowles
    @type ignore_secondaries: bool
908 95ab4de9 David Knowles
    @param ignore_secondaries: if True, ignores errors for the secondary node
909 95ab4de9 David Knowles
        while re-assembling disks (in hard-reboot mode only)
910 95ab4de9 David Knowles
    @type dry_run: bool
911 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
912 d914c76f Simeon Miteff
    @rtype: string
913 d914c76f Simeon Miteff
    @return: job id
914 95ab4de9 David Knowles

915 95ab4de9 David Knowles
    """
916 95ab4de9 David Knowles
    query = []
917 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
918 4c864b55 Michael Hanselmann
    _AppendIf(query, reboot_type, ("type", reboot_type))
919 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_secondaries is not None,
920 4c864b55 Michael Hanselmann
              ("ignore_secondaries", ignore_secondaries))
921 95ab4de9 David Knowles
922 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
923 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reboot" %
924 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
925 95ab4de9 David Knowles
926 b8481ebf Guido Trotter
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False,
927 b8481ebf Guido Trotter
                       **kwargs):
928 95ab4de9 David Knowles
    """Shuts down an instance.
929 95ab4de9 David Knowles

930 95ab4de9 David Knowles
    @type instance: str
931 95ab4de9 David Knowles
    @param instance: the instance to shut down
932 95ab4de9 David Knowles
    @type dry_run: bool
933 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
934 2ba39b8f Iustin Pop
    @type no_remember: bool
935 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
936 d914c76f Simeon Miteff
    @rtype: string
937 d914c76f Simeon Miteff
    @return: job id
938 95ab4de9 David Knowles

939 95ab4de9 David Knowles
    """
940 95ab4de9 David Knowles
    query = []
941 b8481ebf Guido Trotter
    body = kwargs
942 b8481ebf Guido Trotter
943 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
944 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
945 95ab4de9 David Knowles
946 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
947 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/shutdown" %
948 b8481ebf Guido Trotter
                              (GANETI_RAPI_VERSION, instance)), query, body)
949 95ab4de9 David Knowles
950 2ba39b8f Iustin Pop
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
951 95ab4de9 David Knowles
    """Starts up an instance.
952 95ab4de9 David Knowles

953 95ab4de9 David Knowles
    @type instance: str
954 95ab4de9 David Knowles
    @param instance: the instance to start up
955 95ab4de9 David Knowles
    @type dry_run: bool
956 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
957 2ba39b8f Iustin Pop
    @type no_remember: bool
958 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
959 d914c76f Simeon Miteff
    @rtype: string
960 d914c76f Simeon Miteff
    @return: job id
961 95ab4de9 David Knowles

962 95ab4de9 David Knowles
    """
963 95ab4de9 David Knowles
    query = []
964 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
965 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
966 95ab4de9 David Knowles
967 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
968 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/startup" %
969 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
970 95ab4de9 David Knowles
971 c744425f Michael Hanselmann
  def ReinstallInstance(self, instance, os=None, no_startup=False,
972 c744425f Michael Hanselmann
                        osparams=None):
973 95ab4de9 David Knowles
    """Reinstalls an instance.
974 95ab4de9 David Knowles

975 95ab4de9 David Knowles
    @type instance: str
976 fcee9675 David Knowles
    @param instance: The instance to reinstall
977 fcee9675 David Knowles
    @type os: str or None
978 fcee9675 David Knowles
    @param os: The operating system to reinstall. If None, the instance's
979 fcee9675 David Knowles
        current operating system will be installed again
980 95ab4de9 David Knowles
    @type no_startup: bool
981 fcee9675 David Knowles
    @param no_startup: Whether to start the instance automatically
982 d914c76f Simeon Miteff
    @rtype: string
983 d914c76f Simeon Miteff
    @return: job id
984 95ab4de9 David Knowles

985 95ab4de9 David Knowles
    """
986 c744425f Michael Hanselmann
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
987 c744425f Michael Hanselmann
      body = {
988 c744425f Michael Hanselmann
        "start": not no_startup,
989 c744425f Michael Hanselmann
        }
990 57d8e228 Michael Hanselmann
      _SetItemIf(body, os is not None, "os", os)
991 57d8e228 Michael Hanselmann
      _SetItemIf(body, osparams is not None, "osparams", osparams)
992 c744425f Michael Hanselmann
      return self._SendRequest(HTTP_POST,
993 c744425f Michael Hanselmann
                               ("/%s/instances/%s/reinstall" %
994 c744425f Michael Hanselmann
                                (GANETI_RAPI_VERSION, instance)), None, body)
995 c744425f Michael Hanselmann
996 c744425f Michael Hanselmann
    # Use old request format
997 c744425f Michael Hanselmann
    if osparams:
998 c744425f Michael Hanselmann
      raise GanetiApiError("Server does not support specifying OS parameters"
999 c744425f Michael Hanselmann
                           " for instance reinstallation")
1000 c744425f Michael Hanselmann
1001 fcee9675 David Knowles
    query = []
1002 4c864b55 Michael Hanselmann
    _AppendIf(query, os, ("os", os))
1003 4c864b55 Michael Hanselmann
    _AppendIf(query, no_startup, ("nostartup", 1))
1004 4c864b55 Michael Hanselmann
1005 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1006 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reinstall" %
1007 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1008 95ab4de9 David Knowles
1009 bfc2002f Michael Hanselmann
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1010 539d65ba Michael Hanselmann
                           remote_node=None, iallocator=None):
1011 95ab4de9 David Knowles
    """Replaces disks on an instance.
1012 95ab4de9 David Knowles

1013 95ab4de9 David Knowles
    @type instance: str
1014 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1015 bfc2002f Michael Hanselmann
    @type disks: list of ints
1016 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1017 95ab4de9 David Knowles
    @type mode: str
1018 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1019 95ab4de9 David Knowles
    @type remote_node: str or None
1020 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1021 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1022 95ab4de9 David Knowles
    @type iallocator: str or None
1023 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1024 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1025 95ab4de9 David Knowles

1026 98805538 Michael Hanselmann
    @rtype: string
1027 95ab4de9 David Knowles
    @return: job id
1028 95ab4de9 David Knowles

1029 95ab4de9 David Knowles
    """
1030 cfc03c54 Michael Hanselmann
    query = [
1031 cfc03c54 Michael Hanselmann
      ("mode", mode),
1032 cfc03c54 Michael Hanselmann
      ]
1033 95ab4de9 David Knowles
1034 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1035 539d65ba Michael Hanselmann
1036 539d65ba Michael Hanselmann
    if disks is not None:
1037 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1038 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1039 bfc2002f Michael Hanselmann
1040 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1041 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1042 bfc2002f Michael Hanselmann
1043 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1044 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1045 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1046 95ab4de9 David Knowles
1047 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1048 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1049 ebeb600f Michael Hanselmann

1050 ebeb600f Michael Hanselmann
    @type instance: string
1051 ebeb600f Michael Hanselmann
    @param instance: Instance name
1052 ebeb600f Michael Hanselmann
    @type mode: string
1053 ebeb600f Michael Hanselmann
    @param mode: Export mode
1054 ebeb600f Michael Hanselmann
    @rtype: string
1055 ebeb600f Michael Hanselmann
    @return: Job ID
1056 ebeb600f Michael Hanselmann

1057 ebeb600f Michael Hanselmann
    """
1058 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1059 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1060 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1061 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1062 ebeb600f Michael Hanselmann
1063 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1064 ebeb600f Michael Hanselmann
                     remove_instance=None,
1065 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1066 ebeb600f Michael Hanselmann
    """Exports an instance.
1067 ebeb600f Michael Hanselmann

1068 ebeb600f Michael Hanselmann
    @type instance: string
1069 ebeb600f Michael Hanselmann
    @param instance: Instance name
1070 ebeb600f Michael Hanselmann
    @type mode: string
1071 ebeb600f Michael Hanselmann
    @param mode: Export mode
1072 ebeb600f Michael Hanselmann
    @rtype: string
1073 ebeb600f Michael Hanselmann
    @return: Job ID
1074 ebeb600f Michael Hanselmann

1075 ebeb600f Michael Hanselmann
    """
1076 ebeb600f Michael Hanselmann
    body = {
1077 ebeb600f Michael Hanselmann
      "destination": destination,
1078 ebeb600f Michael Hanselmann
      "mode": mode,
1079 ebeb600f Michael Hanselmann
      }
1080 ebeb600f Michael Hanselmann
1081 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1082 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1083 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1084 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1085 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1086 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1087 ebeb600f Michael Hanselmann
1088 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1089 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1090 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1091 ebeb600f Michael Hanselmann
1092 e0ac6ce6 Michael Hanselmann
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1093 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1094 e0ac6ce6 Michael Hanselmann

1095 e0ac6ce6 Michael Hanselmann
    @type instance: string
1096 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1097 e0ac6ce6 Michael Hanselmann
    @type mode: string
1098 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1099 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1100 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1101 d914c76f Simeon Miteff
    @rtype: string
1102 d914c76f Simeon Miteff
    @return: job id
1103 e0ac6ce6 Michael Hanselmann

1104 e0ac6ce6 Michael Hanselmann
    """
1105 e0ac6ce6 Michael Hanselmann
    body = {}
1106 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1107 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1108 e0ac6ce6 Michael Hanselmann
1109 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1110 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1111 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1112 e0ac6ce6 Michael Hanselmann
1113 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1114 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1115 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1116 c0a146a1 Michael Hanselmann

1117 c0a146a1 Michael Hanselmann
    @type instance: string
1118 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1119 c0a146a1 Michael Hanselmann
    @type iallocator: string
1120 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1121 c0a146a1 Michael Hanselmann
      shared-storage instances
1122 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1123 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1124 c0a146a1 Michael Hanselmann
    @type target_node: string
1125 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1126 c0a146a1 Michael Hanselmann
    @rtype: string
1127 c0a146a1 Michael Hanselmann
    @return: job id
1128 c0a146a1 Michael Hanselmann

1129 c0a146a1 Michael Hanselmann
    """
1130 c0a146a1 Michael Hanselmann
    body = {}
1131 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1132 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1133 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1134 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1135 c0a146a1 Michael Hanselmann
1136 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1137 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1138 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1139 c0a146a1 Michael Hanselmann
1140 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1141 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1142 d654aae1 Michael Hanselmann

1143 d654aae1 Michael Hanselmann
    @type instance: string
1144 d654aae1 Michael Hanselmann
    @param instance: Instance name
1145 d654aae1 Michael Hanselmann
    @type new_name: string
1146 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1147 d654aae1 Michael Hanselmann
    @type ip_check: bool
1148 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1149 d654aae1 Michael Hanselmann
    @type name_check: bool
1150 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1151 d914c76f Simeon Miteff
    @rtype: string
1152 d914c76f Simeon Miteff
    @return: job id
1153 d654aae1 Michael Hanselmann

1154 d654aae1 Michael Hanselmann
    """
1155 d654aae1 Michael Hanselmann
    body = {
1156 d654aae1 Michael Hanselmann
      "new_name": new_name,
1157 d654aae1 Michael Hanselmann
      }
1158 d654aae1 Michael Hanselmann
1159 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1160 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1161 d654aae1 Michael Hanselmann
1162 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1163 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1164 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1165 d654aae1 Michael Hanselmann
1166 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1167 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1168 b82d4c5e Michael Hanselmann

1169 b82d4c5e Michael Hanselmann
    @type instance: string
1170 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1171 d914c76f Simeon Miteff
    @rtype: dict
1172 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1173 b82d4c5e Michael Hanselmann

1174 b82d4c5e Michael Hanselmann
    """
1175 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1176 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1177 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1178 b82d4c5e Michael Hanselmann
1179 95ab4de9 David Knowles
  def GetJobs(self):
1180 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1181 95ab4de9 David Knowles

1182 95ab4de9 David Knowles
    @rtype: list of int
1183 95ab4de9 David Knowles
    @return: job ids for the cluster
1184 95ab4de9 David Knowles

1185 95ab4de9 David Knowles
    """
1186 768747ed Michael Hanselmann
    return [int(j["id"])
1187 a198b2d9 Michael Hanselmann
            for j in self._SendRequest(HTTP_GET,
1188 a198b2d9 Michael Hanselmann
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1189 a198b2d9 Michael Hanselmann
                                       None, None)]
1190 95ab4de9 David Knowles
1191 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1192 95ab4de9 David Knowles
    """Gets the status of a job.
1193 95ab4de9 David Knowles

1194 98805538 Michael Hanselmann
    @type job_id: string
1195 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1196 95ab4de9 David Knowles

1197 95ab4de9 David Knowles
    @rtype: dict
1198 95ab4de9 David Knowles
    @return: job status
1199 95ab4de9 David Knowles

1200 95ab4de9 David Knowles
    """
1201 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1202 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1203 a198b2d9 Michael Hanselmann
                             None, None)
1204 95ab4de9 David Knowles
1205 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1206 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1207 16c13387 Theo Van Dinter

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

1211 cfda0e48 Iustin Pop
    @type job_id: string
1212 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1213 16c13387 Theo Van Dinter
    @type period: int
1214 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1215 16c13387 Theo Van Dinter
    @type retries: int
1216 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1217 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1218 16c13387 Theo Van Dinter

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

1225 16c13387 Theo Van Dinter
    """
1226 16c13387 Theo Van Dinter
    while retries != 0:
1227 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1228 dde0e97c Michael Hanselmann
1229 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1230 16c13387 Theo Van Dinter
        return True
1231 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1232 dde0e97c Michael Hanselmann
        return False
1233 dde0e97c Michael Hanselmann
1234 dde0e97c Michael Hanselmann
      if period:
1235 dde0e97c Michael Hanselmann
        time.sleep(period)
1236 dde0e97c Michael Hanselmann
1237 16c13387 Theo Van Dinter
      if retries > 0:
1238 16c13387 Theo Van Dinter
        retries -= 1
1239 dde0e97c Michael Hanselmann
1240 16c13387 Theo Van Dinter
    return False
1241 16c13387 Theo Van Dinter
1242 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1243 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1244 d9b67f70 Michael Hanselmann

1245 98805538 Michael Hanselmann
    @type job_id: string
1246 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1247 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1248 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1249 d914c76f Simeon Miteff
    @rtype: dict
1250 d9b67f70 Michael Hanselmann

1251 d9b67f70 Michael Hanselmann
    """
1252 d9b67f70 Michael Hanselmann
    body = {
1253 d9b67f70 Michael Hanselmann
      "fields": fields,
1254 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1255 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1256 d9b67f70 Michael Hanselmann
      }
1257 d9b67f70 Michael Hanselmann
1258 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1259 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1260 a198b2d9 Michael Hanselmann
                             None, body)
1261 d9b67f70 Michael Hanselmann
1262 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1263 cf9ada49 Michael Hanselmann
    """Cancels a job.
1264 95ab4de9 David Knowles

1265 98805538 Michael Hanselmann
    @type job_id: string
1266 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1267 95ab4de9 David Knowles
    @type dry_run: bool
1268 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1269 d914c76f Simeon Miteff
    @rtype: tuple
1270 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1271 95ab4de9 David Knowles

1272 95ab4de9 David Knowles
    """
1273 95ab4de9 David Knowles
    query = []
1274 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1275 95ab4de9 David Knowles
1276 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1277 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1278 a198b2d9 Michael Hanselmann
                             query, None)
1279 95ab4de9 David Knowles
1280 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1281 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1282 95ab4de9 David Knowles

1283 95ab4de9 David Knowles
    @type bulk: bool
1284 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1285 95ab4de9 David Knowles

1286 95ab4de9 David Knowles
    @rtype: list of dict or str
1287 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1288 95ab4de9 David Knowles
        else list of nodes in the cluster
1289 95ab4de9 David Knowles

1290 95ab4de9 David Knowles
    """
1291 95ab4de9 David Knowles
    query = []
1292 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1293 95ab4de9 David Knowles
1294 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1295 a198b2d9 Michael Hanselmann
                              query, None)
1296 95ab4de9 David Knowles
    if bulk:
1297 95ab4de9 David Knowles
      return nodes
1298 95ab4de9 David Knowles
    else:
1299 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1300 95ab4de9 David Knowles
1301 591e5103 Michael Hanselmann
  def GetNode(self, node):
1302 95ab4de9 David Knowles
    """Gets information about a node.
1303 95ab4de9 David Knowles

1304 95ab4de9 David Knowles
    @type node: str
1305 95ab4de9 David Knowles
    @param node: node whose info to return
1306 95ab4de9 David Knowles

1307 95ab4de9 David Knowles
    @rtype: dict
1308 95ab4de9 David Knowles
    @return: info about the node
1309 95ab4de9 David Knowles

1310 95ab4de9 David Knowles
    """
1311 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1312 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1313 a198b2d9 Michael Hanselmann
                             None, None)
1314 95ab4de9 David Knowles
1315 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1316 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1317 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1318 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1319 95ab4de9 David Knowles

1320 95ab4de9 David Knowles
    @type node: str
1321 95ab4de9 David Knowles
    @param node: node to evacuate
1322 95ab4de9 David Knowles
    @type iallocator: str or None
1323 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1324 95ab4de9 David Knowles
    @type remote_node: str
1325 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1326 95ab4de9 David Knowles
    @type dry_run: bool
1327 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1328 941b9309 Iustin Pop
    @type early_release: bool
1329 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1330 0b58db81 Michael Hanselmann
    @type mode: string
1331 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1332 de40437a Michael Hanselmann
    @type accept_old: bool
1333 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1334 de40437a Michael Hanselmann
        results
1335 de40437a Michael Hanselmann

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

1342 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1343 941b9309 Iustin Pop
        specified
1344 95ab4de9 David Knowles

1345 95ab4de9 David Knowles
    """
1346 95ab4de9 David Knowles
    if iallocator and remote_node:
1347 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1348 95ab4de9 David Knowles
1349 cfc03c54 Michael Hanselmann
    query = []
1350 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1351 de40437a Michael Hanselmann
1352 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1353 0b58db81 Michael Hanselmann
      # Server supports body parameters
1354 de40437a Michael Hanselmann
      body = {}
1355 de40437a Michael Hanselmann
1356 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1357 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1358 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1359 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1360 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1361 de40437a Michael Hanselmann
    else:
1362 de40437a Michael Hanselmann
      # Pre-2.5 request format
1363 de40437a Michael Hanselmann
      body = None
1364 de40437a Michael Hanselmann
1365 de40437a Michael Hanselmann
      if not accept_old:
1366 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1367 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1368 de40437a Michael Hanselmann
                             " accept_old)")
1369 de40437a Michael Hanselmann
1370 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1371 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1372 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1373 de40437a Michael Hanselmann
1374 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1375 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1376 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1377 95ab4de9 David Knowles
1378 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1379 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1380 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1381 95ab4de9 David Knowles
1382 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1383 b7a1c816 Michael Hanselmann
                  target_node=None):
1384 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1385 95ab4de9 David Knowles

1386 95ab4de9 David Knowles
    @type node: str
1387 95ab4de9 David Knowles
    @param node: node to migrate
1388 1f334d96 Iustin Pop
    @type mode: string
1389 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1390 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1391 95ab4de9 David Knowles
    @type dry_run: bool
1392 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1393 b7a1c816 Michael Hanselmann
    @type iallocator: string
1394 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1395 b7a1c816 Michael Hanselmann
    @type target_node: string
1396 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1397 95ab4de9 David Knowles

1398 98805538 Michael Hanselmann
    @rtype: string
1399 95ab4de9 David Knowles
    @return: job id
1400 95ab4de9 David Knowles

1401 95ab4de9 David Knowles
    """
1402 95ab4de9 David Knowles
    query = []
1403 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1404 95ab4de9 David Knowles
1405 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1406 b7a1c816 Michael Hanselmann
      body = {}
1407 b7a1c816 Michael Hanselmann
1408 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1409 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1410 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1411 b7a1c816 Michael Hanselmann
1412 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1413 b7a1c816 Michael Hanselmann
1414 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1415 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1416 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1417 b7a1c816 Michael Hanselmann
    else:
1418 b7a1c816 Michael Hanselmann
      # Use old request format
1419 b7a1c816 Michael Hanselmann
      if target_node is not None:
1420 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1421 b7a1c816 Michael Hanselmann
                             " for node migration")
1422 b7a1c816 Michael Hanselmann
1423 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1424 b7a1c816 Michael Hanselmann
1425 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1426 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1427 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1428 95ab4de9 David Knowles
1429 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1430 95ab4de9 David Knowles
    """Gets the current role for a node.
1431 95ab4de9 David Knowles

1432 95ab4de9 David Knowles
    @type node: str
1433 95ab4de9 David Knowles
    @param node: node whose role to return
1434 95ab4de9 David Knowles

1435 95ab4de9 David Knowles
    @rtype: str
1436 95ab4de9 David Knowles
    @return: the current role for a node
1437 95ab4de9 David Knowles

1438 95ab4de9 David Knowles
    """
1439 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1440 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1441 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1442 95ab4de9 David Knowles
1443 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1444 95ab4de9 David Knowles
    """Sets the role for a node.
1445 95ab4de9 David Knowles

1446 95ab4de9 David Knowles
    @type node: str
1447 95ab4de9 David Knowles
    @param node: the node whose role to set
1448 95ab4de9 David Knowles
    @type role: str
1449 95ab4de9 David Knowles
    @param role: the role to set for the node
1450 95ab4de9 David Knowles
    @type force: bool
1451 95ab4de9 David Knowles
    @param force: whether to force the role change
1452 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1453 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1454 8de8e68d Michael Hanselmann
                         if necessary
1455 95ab4de9 David Knowles

1456 98805538 Michael Hanselmann
    @rtype: string
1457 95ab4de9 David Knowles
    @return: job id
1458 95ab4de9 David Knowles

1459 95ab4de9 David Knowles
    """
1460 4c864b55 Michael Hanselmann
    query = []
1461 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1462 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1463 8de8e68d Michael Hanselmann
1464 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1465 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1466 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1467 95ab4de9 David Knowles
1468 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1469 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1470 42d4d8b9 Michael Hanselmann

1471 42d4d8b9 Michael Hanselmann
    @type node: string
1472 42d4d8b9 Michael Hanselmann
    @param node: Node name
1473 42d4d8b9 Michael Hanselmann
    @type force: bool
1474 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1475 42d4d8b9 Michael Hanselmann
    @rtype: string
1476 42d4d8b9 Michael Hanselmann
    @return: job id
1477 42d4d8b9 Michael Hanselmann

1478 42d4d8b9 Michael Hanselmann
    """
1479 4c864b55 Michael Hanselmann
    query = []
1480 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1481 42d4d8b9 Michael Hanselmann
1482 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1483 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1484 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1485 42d4d8b9 Michael Hanselmann
1486 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1487 94497dd1 Michael Hanselmann
    """Modifies a node.
1488 94497dd1 Michael Hanselmann

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

1491 370f2042 Guido Trotter
    @type node: string
1492 370f2042 Guido Trotter
    @param node: Node name
1493 94497dd1 Michael Hanselmann
    @rtype: string
1494 94497dd1 Michael Hanselmann
    @return: job id
1495 94497dd1 Michael Hanselmann

1496 94497dd1 Michael Hanselmann
    """
1497 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1498 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1499 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1500 94497dd1 Michael Hanselmann
1501 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1502 95ab4de9 David Knowles
    """Gets the storage units for a node.
1503 95ab4de9 David Knowles

1504 95ab4de9 David Knowles
    @type node: str
1505 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1506 95ab4de9 David Knowles
    @type storage_type: str
1507 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1508 95ab4de9 David Knowles
    @type output_fields: str
1509 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1510 95ab4de9 David Knowles

1511 98805538 Michael Hanselmann
    @rtype: string
1512 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1513 95ab4de9 David Knowles

1514 95ab4de9 David Knowles
    """
1515 cfc03c54 Michael Hanselmann
    query = [
1516 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1517 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1518 cfc03c54 Michael Hanselmann
      ]
1519 95ab4de9 David Knowles
1520 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1521 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1522 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1523 95ab4de9 David Knowles
1524 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1525 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1526 95ab4de9 David Knowles

1527 95ab4de9 David Knowles
    @type node: str
1528 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1529 95ab4de9 David Knowles
    @type storage_type: str
1530 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1531 95ab4de9 David Knowles
    @type name: str
1532 95ab4de9 David Knowles
    @param name: name of the storage unit
1533 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1534 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1535 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1536 95ab4de9 David Knowles

1537 98805538 Michael Hanselmann
    @rtype: string
1538 95ab4de9 David Knowles
    @return: job id
1539 95ab4de9 David Knowles

1540 95ab4de9 David Knowles
    """
1541 95ab4de9 David Knowles
    query = [
1542 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1543 cfc03c54 Michael Hanselmann
      ("name", name),
1544 cfc03c54 Michael Hanselmann
      ]
1545 cfc03c54 Michael Hanselmann
1546 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1547 fde28316 Michael Hanselmann
1548 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1549 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1550 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1551 95ab4de9 David Knowles
1552 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1553 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1554 95ab4de9 David Knowles

1555 95ab4de9 David Knowles
    @type node: str
1556 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1557 95ab4de9 David Knowles
    @type storage_type: str
1558 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1559 95ab4de9 David Knowles
    @type name: str
1560 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1561 95ab4de9 David Knowles

1562 98805538 Michael Hanselmann
    @rtype: string
1563 95ab4de9 David Knowles
    @return: job id
1564 95ab4de9 David Knowles

1565 95ab4de9 David Knowles
    """
1566 cfc03c54 Michael Hanselmann
    query = [
1567 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1568 cfc03c54 Michael Hanselmann
      ("name", name),
1569 cfc03c54 Michael Hanselmann
      ]
1570 95ab4de9 David Knowles
1571 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1572 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1573 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1574 95ab4de9 David Knowles
1575 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1576 95ab4de9 David Knowles
    """Gets the tags for a node.
1577 95ab4de9 David Knowles

1578 95ab4de9 David Knowles
    @type node: str
1579 95ab4de9 David Knowles
    @param node: node whose tags to return
1580 95ab4de9 David Knowles

1581 95ab4de9 David Knowles
    @rtype: list of str
1582 95ab4de9 David Knowles
    @return: tags for the node
1583 95ab4de9 David Knowles

1584 95ab4de9 David Knowles
    """
1585 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1586 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1587 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1588 95ab4de9 David Knowles
1589 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1590 95ab4de9 David Knowles
    """Adds tags to a node.
1591 95ab4de9 David Knowles

1592 95ab4de9 David Knowles
    @type node: str
1593 95ab4de9 David Knowles
    @param node: node to add tags to
1594 95ab4de9 David Knowles
    @type tags: list of str
1595 95ab4de9 David Knowles
    @param tags: tags to add to the node
1596 95ab4de9 David Knowles
    @type dry_run: bool
1597 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1598 95ab4de9 David Knowles

1599 98805538 Michael Hanselmann
    @rtype: string
1600 95ab4de9 David Knowles
    @return: job id
1601 95ab4de9 David Knowles

1602 95ab4de9 David Knowles
    """
1603 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1604 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1605 95ab4de9 David Knowles
1606 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1607 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1608 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1609 95ab4de9 David Knowles
1610 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1611 95ab4de9 David Knowles
    """Delete tags from a node.
1612 95ab4de9 David Knowles

1613 95ab4de9 David Knowles
    @type node: str
1614 95ab4de9 David Knowles
    @param node: node to remove tags from
1615 95ab4de9 David Knowles
    @type tags: list of str
1616 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1617 95ab4de9 David Knowles
    @type dry_run: bool
1618 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1619 95ab4de9 David Knowles

1620 98805538 Michael Hanselmann
    @rtype: string
1621 95ab4de9 David Knowles
    @return: job id
1622 95ab4de9 David Knowles

1623 95ab4de9 David Knowles
    """
1624 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1625 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1626 95ab4de9 David Knowles
1627 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1628 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1629 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1630 a268af8d Adeodato Simo
1631 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1632 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1633 a268af8d Adeodato Simo

1634 a268af8d Adeodato Simo
    @type bulk: bool
1635 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1636 a268af8d Adeodato Simo

1637 a268af8d Adeodato Simo
    @rtype: list of dict or str
1638 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1639 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1640 a268af8d Adeodato Simo

1641 a268af8d Adeodato Simo
    """
1642 a268af8d Adeodato Simo
    query = []
1643 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1644 a268af8d Adeodato Simo
1645 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1646 a268af8d Adeodato Simo
                               query, None)
1647 a268af8d Adeodato Simo
    if bulk:
1648 a268af8d Adeodato Simo
      return groups
1649 a268af8d Adeodato Simo
    else:
1650 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1651 a268af8d Adeodato Simo
1652 a268af8d Adeodato Simo
  def GetGroup(self, group):
1653 a268af8d Adeodato Simo
    """Gets information about a node group.
1654 a268af8d Adeodato Simo

1655 a268af8d Adeodato Simo
    @type group: str
1656 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1657 a268af8d Adeodato Simo

1658 a268af8d Adeodato Simo
    @rtype: dict
1659 a268af8d Adeodato Simo
    @return: info about the node group
1660 a268af8d Adeodato Simo

1661 a268af8d Adeodato Simo
    """
1662 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1663 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1664 a268af8d Adeodato Simo
                             None, None)
1665 a268af8d Adeodato Simo
1666 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1667 a268af8d Adeodato Simo
    """Creates a new node group.
1668 a268af8d Adeodato Simo

1669 a268af8d Adeodato Simo
    @type name: str
1670 a268af8d Adeodato Simo
    @param name: the name of node group to create
1671 90e99856 Adeodato Simo
    @type alloc_policy: str
1672 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
1673 a268af8d Adeodato Simo
    @type dry_run: bool
1674 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1675 a268af8d Adeodato Simo

1676 98805538 Michael Hanselmann
    @rtype: string
1677 a268af8d Adeodato Simo
    @return: job id
1678 a268af8d Adeodato Simo

1679 a268af8d Adeodato Simo
    """
1680 a268af8d Adeodato Simo
    query = []
1681 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1682 a268af8d Adeodato Simo
1683 a268af8d Adeodato Simo
    body = {
1684 a268af8d Adeodato Simo
      "name": name,
1685 90e99856 Adeodato Simo
      "alloc_policy": alloc_policy
1686 a268af8d Adeodato Simo
      }
1687 a268af8d Adeodato Simo
1688 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1689 a268af8d Adeodato Simo
                             query, body)
1690 a268af8d Adeodato Simo
1691 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
1692 f18fab7d Adeodato Simo
    """Modifies a node group.
1693 f18fab7d Adeodato Simo

1694 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
1695 f18fab7d Adeodato Simo

1696 f18fab7d Adeodato Simo
    @type group: string
1697 f18fab7d Adeodato Simo
    @param group: Node group name
1698 98805538 Michael Hanselmann
    @rtype: string
1699 f18fab7d Adeodato Simo
    @return: job id
1700 f18fab7d Adeodato Simo

1701 f18fab7d Adeodato Simo
    """
1702 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1703 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
1704 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1705 f18fab7d Adeodato Simo
1706 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
1707 a268af8d Adeodato Simo
    """Deletes a node group.
1708 a268af8d Adeodato Simo

1709 a268af8d Adeodato Simo
    @type group: str
1710 a268af8d Adeodato Simo
    @param group: the node group to delete
1711 a268af8d Adeodato Simo
    @type dry_run: bool
1712 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1713 a268af8d Adeodato Simo

1714 98805538 Michael Hanselmann
    @rtype: string
1715 a268af8d Adeodato Simo
    @return: job id
1716 a268af8d Adeodato Simo

1717 a268af8d Adeodato Simo
    """
1718 a268af8d Adeodato Simo
    query = []
1719 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1720 a268af8d Adeodato Simo
1721 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
1722 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
1723 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
1724 a268af8d Adeodato Simo
1725 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
1726 a268af8d Adeodato Simo
    """Changes the name of a node group.
1727 a268af8d Adeodato Simo

1728 a268af8d Adeodato Simo
    @type group: string
1729 a268af8d Adeodato Simo
    @param group: Node group name
1730 a268af8d Adeodato Simo
    @type new_name: string
1731 a268af8d Adeodato Simo
    @param new_name: New node group name
1732 a268af8d Adeodato Simo

1733 98805538 Michael Hanselmann
    @rtype: string
1734 a268af8d Adeodato Simo
    @return: job id
1735 a268af8d Adeodato Simo

1736 a268af8d Adeodato Simo
    """
1737 a268af8d Adeodato Simo
    body = {
1738 a268af8d Adeodato Simo
      "new_name": new_name,
1739 a268af8d Adeodato Simo
      }
1740 a268af8d Adeodato Simo
1741 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1742 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
1743 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
1744 4245446f Adeodato Simo
1745 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
1746 4245446f Adeodato Simo
    """Assigns nodes to a group.
1747 4245446f Adeodato Simo

1748 4245446f Adeodato Simo
    @type group: string
1749 6ce90543 Michael Hanselmann
    @param group: Node group name
1750 4245446f Adeodato Simo
    @type nodes: list of strings
1751 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
1752 4245446f Adeodato Simo

1753 98805538 Michael Hanselmann
    @rtype: string
1754 4245446f Adeodato Simo
    @return: job id
1755 4245446f Adeodato Simo

1756 4245446f Adeodato Simo
    """
1757 4245446f Adeodato Simo
    query = []
1758 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1759 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1760 4245446f Adeodato Simo
1761 4245446f Adeodato Simo
    body = {
1762 4245446f Adeodato Simo
      "nodes": nodes,
1763 4245446f Adeodato Simo
      }
1764 4245446f Adeodato Simo
1765 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1766 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
1767 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
1768 208a6cff Michael Hanselmann
1769 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
1770 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
1771 414ebaf1 Michael Hanselmann

1772 414ebaf1 Michael Hanselmann
    @type group: string
1773 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
1774 414ebaf1 Michael Hanselmann

1775 414ebaf1 Michael Hanselmann
    @rtype: list of strings
1776 414ebaf1 Michael Hanselmann
    @return: tags for the group
1777 414ebaf1 Michael Hanselmann

1778 414ebaf1 Michael Hanselmann
    """
1779 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1780 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1781 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
1782 414ebaf1 Michael Hanselmann
1783 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
1784 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
1785 414ebaf1 Michael Hanselmann

1786 414ebaf1 Michael Hanselmann
    @type group: str
1787 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
1788 414ebaf1 Michael Hanselmann
    @type tags: list of string
1789 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
1790 414ebaf1 Michael Hanselmann
    @type dry_run: bool
1791 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
1792 414ebaf1 Michael Hanselmann

1793 414ebaf1 Michael Hanselmann
    @rtype: string
1794 414ebaf1 Michael Hanselmann
    @return: job id
1795 414ebaf1 Michael Hanselmann

1796 414ebaf1 Michael Hanselmann
    """
1797 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
1798 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1799 414ebaf1 Michael Hanselmann
1800 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1801 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1802 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
1803 414ebaf1 Michael Hanselmann
1804 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
1805 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
1806 414ebaf1 Michael Hanselmann

1807 414ebaf1 Michael Hanselmann
    @type group: str
1808 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
1809 414ebaf1 Michael Hanselmann
    @type tags: list of string
1810 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
1811 414ebaf1 Michael Hanselmann
    @type dry_run: bool
1812 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
1813 414ebaf1 Michael Hanselmann
    @rtype: string
1814 414ebaf1 Michael Hanselmann
    @return: job id
1815 414ebaf1 Michael Hanselmann

1816 414ebaf1 Michael Hanselmann
    """
1817 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
1818 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1819 414ebaf1 Michael Hanselmann
1820 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1821 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
1822 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
1823 414ebaf1 Michael Hanselmann
1824 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
1825 208a6cff Michael Hanselmann
    """Retrieves information about resources.
1826 208a6cff Michael Hanselmann

1827 208a6cff Michael Hanselmann
    @type what: string
1828 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1829 208a6cff Michael Hanselmann
    @type fields: list of string
1830 208a6cff Michael Hanselmann
    @param fields: Requested fields
1831 2e5c33db Iustin Pop
    @type qfilter: None or list
1832 2e5c33db Iustin Pop
    @param qfilter: Query filter
1833 208a6cff Michael Hanselmann

1834 208a6cff Michael Hanselmann
    @rtype: string
1835 208a6cff Michael Hanselmann
    @return: job id
1836 208a6cff Michael Hanselmann

1837 208a6cff Michael Hanselmann
    """
1838 208a6cff Michael Hanselmann
    body = {
1839 208a6cff Michael Hanselmann
      "fields": fields,
1840 208a6cff Michael Hanselmann
      }
1841 208a6cff Michael Hanselmann
1842 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
1843 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
1844 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
1845 208a6cff Michael Hanselmann
1846 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1847 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
1848 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
1849 208a6cff Michael Hanselmann
1850 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
1851 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
1852 208a6cff Michael Hanselmann

1853 208a6cff Michael Hanselmann
    @type what: string
1854 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1855 208a6cff Michael Hanselmann
    @type fields: list of string
1856 208a6cff Michael Hanselmann
    @param fields: Requested fields
1857 208a6cff Michael Hanselmann

1858 208a6cff Michael Hanselmann
    @rtype: string
1859 208a6cff Michael Hanselmann
    @return: job id
1860 208a6cff Michael Hanselmann

1861 208a6cff Michael Hanselmann
    """
1862 208a6cff Michael Hanselmann
    query = []
1863 208a6cff Michael Hanselmann
1864 208a6cff Michael Hanselmann
    if fields is not None:
1865 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
1866 208a6cff Michael Hanselmann
1867 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1868 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
1869 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)