Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / client.py @ 1fa2c40b

History | View | Annotate | Download (61.6 kB)

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

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

31 2a7c3583 Michael Hanselmann
"""
32 95ab4de9 David Knowles
33 5ef5cfea Michael Hanselmann
# No Ganeti-specific modules should be imported. The RAPI client is supposed to
34 5ef5cfea Michael Hanselmann
# be standalone.
35 5ef5cfea Michael Hanselmann
36 9279e986 Michael Hanselmann
import logging
37 95ab4de9 David Knowles
import simplejson
38 1a8337f2 Manuel Franceschini
import socket
39 95ab4de9 David Knowles
import urllib
40 2a7c3583 Michael Hanselmann
import threading
41 2a7c3583 Michael Hanselmann
import pycurl
42 16c13387 Theo Van Dinter
import time
43 2a7c3583 Michael Hanselmann
44 2a7c3583 Michael Hanselmann
try:
45 2a7c3583 Michael Hanselmann
  from cStringIO import StringIO
46 2a7c3583 Michael Hanselmann
except ImportError:
47 2a7c3583 Michael Hanselmann
  from StringIO import StringIO
48 95ab4de9 David Knowles
49 95ab4de9 David Knowles
50 9279e986 Michael Hanselmann
GANETI_RAPI_PORT = 5080
51 a198b2d9 Michael Hanselmann
GANETI_RAPI_VERSION = 2
52 9279e986 Michael Hanselmann
53 95ab4de9 David Knowles
HTTP_DELETE = "DELETE"
54 95ab4de9 David Knowles
HTTP_GET = "GET"
55 95ab4de9 David Knowles
HTTP_PUT = "PUT"
56 95ab4de9 David Knowles
HTTP_POST = "POST"
57 9279e986 Michael Hanselmann
HTTP_OK = 200
58 7eac4a4d Michael Hanselmann
HTTP_NOT_FOUND = 404
59 9279e986 Michael Hanselmann
HTTP_APP_JSON = "application/json"
60 9279e986 Michael Hanselmann
61 95ab4de9 David Knowles
REPLACE_DISK_PRI = "replace_on_primary"
62 95ab4de9 David Knowles
REPLACE_DISK_SECONDARY = "replace_on_secondary"
63 95ab4de9 David Knowles
REPLACE_DISK_CHG = "replace_new_secondary"
64 95ab4de9 David Knowles
REPLACE_DISK_AUTO = "replace_auto"
65 1068639f Michael Hanselmann
66 0b58db81 Michael Hanselmann
NODE_EVAC_PRI = "primary-only"
67 0b58db81 Michael Hanselmann
NODE_EVAC_SEC = "secondary-only"
68 0b58db81 Michael Hanselmann
NODE_EVAC_ALL = "all"
69 0b58db81 Michael Hanselmann
70 1068639f Michael Hanselmann
NODE_ROLE_DRAINED = "drained"
71 1068639f Michael Hanselmann
NODE_ROLE_MASTER_CANDIATE = "master-candidate"
72 1068639f Michael Hanselmann
NODE_ROLE_MASTER = "master"
73 1068639f Michael Hanselmann
NODE_ROLE_OFFLINE = "offline"
74 1068639f Michael Hanselmann
NODE_ROLE_REGULAR = "regular"
75 95ab4de9 David Knowles
76 63d5eb8a Michael Hanselmann
JOB_STATUS_QUEUED = "queued"
77 47099cd1 Michael Hanselmann
JOB_STATUS_WAITING = "waiting"
78 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELING = "canceling"
79 63d5eb8a Michael Hanselmann
JOB_STATUS_RUNNING = "running"
80 63d5eb8a Michael Hanselmann
JOB_STATUS_CANCELED = "canceled"
81 63d5eb8a Michael Hanselmann
JOB_STATUS_SUCCESS = "success"
82 63d5eb8a Michael Hanselmann
JOB_STATUS_ERROR = "error"
83 1eaddbc6 Michael Hanselmann
JOB_STATUS_PENDING = frozenset([
84 1eaddbc6 Michael Hanselmann
  JOB_STATUS_QUEUED,
85 1eaddbc6 Michael Hanselmann
  JOB_STATUS_WAITING,
86 1eaddbc6 Michael Hanselmann
  JOB_STATUS_CANCELING,
87 1eaddbc6 Michael Hanselmann
  ])
88 63d5eb8a Michael Hanselmann
JOB_STATUS_FINALIZED = frozenset([
89 63d5eb8a Michael Hanselmann
  JOB_STATUS_CANCELED,
90 63d5eb8a Michael Hanselmann
  JOB_STATUS_SUCCESS,
91 63d5eb8a Michael Hanselmann
  JOB_STATUS_ERROR,
92 63d5eb8a Michael Hanselmann
  ])
93 63d5eb8a Michael Hanselmann
JOB_STATUS_ALL = frozenset([
94 63d5eb8a Michael Hanselmann
  JOB_STATUS_RUNNING,
95 1eaddbc6 Michael Hanselmann
  ]) | JOB_STATUS_PENDING | JOB_STATUS_FINALIZED
96 63d5eb8a Michael Hanselmann
97 47099cd1 Michael Hanselmann
# Legacy name
98 47099cd1 Michael Hanselmann
JOB_STATUS_WAITLOCK = JOB_STATUS_WAITING
99 47099cd1 Michael Hanselmann
100 8a47b447 Michael Hanselmann
# Internal constants
101 8a47b447 Michael Hanselmann
_REQ_DATA_VERSION_FIELD = "__version__"
102 4c864b55 Michael Hanselmann
_QPARAM_DRY_RUN = "dry-run"
103 4c864b55 Michael Hanselmann
_QPARAM_FORCE = "force"
104 8a47b447 Michael Hanselmann
105 6396dc04 Michael Hanselmann
# Feature strings
106 6396dc04 Michael Hanselmann
INST_CREATE_REQV1 = "instance-create-reqv1"
107 6396dc04 Michael Hanselmann
INST_REINSTALL_REQV1 = "instance-reinstall-reqv1"
108 6396dc04 Michael Hanselmann
NODE_MIGRATE_REQV1 = "node-migrate-reqv1"
109 6396dc04 Michael Hanselmann
NODE_EVAC_RES1 = "node-evac-res1"
110 6396dc04 Michael Hanselmann
111 6396dc04 Michael Hanselmann
# Old feature constant names in case they're references by users of this module
112 6396dc04 Michael Hanselmann
_INST_CREATE_REQV1 = INST_CREATE_REQV1
113 6396dc04 Michael Hanselmann
_INST_REINSTALL_REQV1 = INST_REINSTALL_REQV1
114 6396dc04 Michael Hanselmann
_NODE_MIGRATE_REQV1 = NODE_MIGRATE_REQV1
115 6396dc04 Michael Hanselmann
_NODE_EVAC_RES1 = NODE_EVAC_RES1
116 6396dc04 Michael Hanselmann
117 2a7c3583 Michael Hanselmann
# Older pycURL versions don't have all error constants
118 2a7c3583 Michael Hanselmann
try:
119 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
120 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
121 2a7c3583 Michael Hanselmann
except AttributeError:
122 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT = 60
123 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE = 77
124 2a7c3583 Michael Hanselmann
125 2a7c3583 Michael Hanselmann
_CURL_SSL_CERT_ERRORS = frozenset([
126 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT,
127 2a7c3583 Michael Hanselmann
  _CURLE_SSL_CACERT_BADFILE,
128 2a7c3583 Michael Hanselmann
  ])
129 2a7c3583 Michael Hanselmann
130 95ab4de9 David Knowles
131 95ab4de9 David Knowles
class Error(Exception):
132 95ab4de9 David Knowles
  """Base error class for this module.
133 95ab4de9 David Knowles

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

436 95ab4de9 David Knowles
    @rtype: str
437 95ab4de9 David Knowles
    @return: JSON-Decoded response
438 95ab4de9 David Knowles

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

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

513 95ab4de9 David Knowles
    @rtype: int
514 f2f88abf David Knowles
    @return: Ganeti Remote API version
515 95ab4de9 David Knowles

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

522 7eac4a4d Michael Hanselmann
    @rtype: list
523 7eac4a4d Michael Hanselmann
    @return: List of optional features
524 7eac4a4d Michael Hanselmann

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

539 95ab4de9 David Knowles
    @rtype: list of str
540 95ab4de9 David Knowles
    @return: operating systems
541 95ab4de9 David Knowles

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

549 95ab4de9 David Knowles
    @rtype: dict
550 95ab4de9 David Knowles
    @return: information about the cluster
551 95ab4de9 David Knowles

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

559 d914c76f Simeon Miteff
    @rtype: string
560 54d4c13b Michael Hanselmann
    @return: job id
561 54d4c13b Michael Hanselmann

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

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

572 98805538 Michael Hanselmann
    @rtype: string
573 62e999a5 Michael Hanselmann
    @return: job id
574 62e999a5 Michael Hanselmann

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

584 95ab4de9 David Knowles
    @rtype: list of str
585 95ab4de9 David Knowles
    @return: cluster tags
586 95ab4de9 David Knowles

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

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

599 98805538 Michael Hanselmann
    @rtype: string
600 95ab4de9 David Knowles
    @return: job id
601 95ab4de9 David Knowles

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

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

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

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

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

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

650 95ab4de9 David Knowles
    @type instance: str
651 95ab4de9 David Knowles
    @param instance: instance whose info to return
652 95ab4de9 David Knowles

653 95ab4de9 David Knowles
    @rtype: dict
654 95ab4de9 David Knowles
    @return: info about the instance
655 95ab4de9 David Knowles

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

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

669 591e5103 Michael Hanselmann
    """
670 591e5103 Michael Hanselmann
    if static is not None:
671 591e5103 Michael Hanselmann
      query = [("static", static)]
672 591e5103 Michael Hanselmann
    else:
673 591e5103 Michael Hanselmann
      query = None
674 591e5103 Michael Hanselmann
675 591e5103 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
676 591e5103 Michael Hanselmann
                             ("/%s/instances/%s/info" %
677 591e5103 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
678 591e5103 Michael Hanselmann
679 915faf72 Renรฉ Nussbaumer
  @staticmethod
680 915faf72 Renรฉ Nussbaumer
  def _UpdateWithKwargs(base, **kwargs):
681 915faf72 Renรฉ Nussbaumer
    """Updates the base with params from kwargs.
682 915faf72 Renรฉ Nussbaumer

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

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

687 915faf72 Renรฉ Nussbaumer
    """
688 915faf72 Renรฉ Nussbaumer
    conflicts = set(kwargs.iterkeys()) & set(base.iterkeys())
689 915faf72 Renรฉ Nussbaumer
    if conflicts:
690 915faf72 Renรฉ Nussbaumer
      raise GanetiApiError("Required fields can not be specified as"
691 915faf72 Renรฉ Nussbaumer
                           " keywords: %s" % ", ".join(conflicts))
692 915faf72 Renรฉ Nussbaumer
693 915faf72 Renรฉ Nussbaumer
    base.update((key, value) for key, value in kwargs.iteritems()
694 915faf72 Renรฉ Nussbaumer
                if key != "dry_run")
695 915faf72 Renรฉ Nussbaumer
696 915faf72 Renรฉ Nussbaumer
  def InstanceAllocation(self, mode, name, disk_template, disks, nics,
697 915faf72 Renรฉ Nussbaumer
                         **kwargs):
698 915faf72 Renรฉ Nussbaumer
    """Generates an instance allocation as used by multiallocate.
699 915faf72 Renรฉ Nussbaumer

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

703 915faf72 Renรฉ Nussbaumer
    @type mode: string
704 915faf72 Renรฉ Nussbaumer
    @param mode: Instance creation mode
705 915faf72 Renรฉ Nussbaumer
    @type name: string
706 915faf72 Renรฉ Nussbaumer
    @param name: Hostname of the instance to create
707 915faf72 Renรฉ Nussbaumer
    @type disk_template: string
708 915faf72 Renรฉ Nussbaumer
    @param disk_template: Disk template for instance (e.g. plain, diskless,
709 915faf72 Renรฉ Nussbaumer
                          file, or drbd)
710 915faf72 Renรฉ Nussbaumer
    @type disks: list of dicts
711 915faf72 Renรฉ Nussbaumer
    @param disks: List of disk definitions
712 915faf72 Renรฉ Nussbaumer
    @type nics: list of dicts
713 915faf72 Renรฉ Nussbaumer
    @param nics: List of NIC definitions
714 915faf72 Renรฉ Nussbaumer

715 915faf72 Renรฉ Nussbaumer
    @return: A dict with the generated entry
716 915faf72 Renรฉ Nussbaumer

717 915faf72 Renรฉ Nussbaumer
    """
718 915faf72 Renรฉ Nussbaumer
    # All required fields for request data version 1
719 915faf72 Renรฉ Nussbaumer
    alloc = {
720 915faf72 Renรฉ Nussbaumer
      "mode": mode,
721 915faf72 Renรฉ Nussbaumer
      "name": name,
722 915faf72 Renรฉ Nussbaumer
      "disk_template": disk_template,
723 915faf72 Renรฉ Nussbaumer
      "disks": disks,
724 915faf72 Renรฉ Nussbaumer
      "nics": nics,
725 915faf72 Renรฉ Nussbaumer
      }
726 915faf72 Renรฉ Nussbaumer
727 915faf72 Renรฉ Nussbaumer
    self._UpdateWithKwargs(alloc, **kwargs)
728 915faf72 Renรฉ Nussbaumer
729 915faf72 Renรฉ Nussbaumer
    return alloc
730 915faf72 Renรฉ Nussbaumer
731 915faf72 Renรฉ Nussbaumer
  def InstancesMultiAlloc(self, instances, **kwargs):
732 915faf72 Renรฉ Nussbaumer
    """Tries to allocate multiple instances.
733 915faf72 Renรฉ Nussbaumer

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

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

738 915faf72 Renรฉ Nussbaumer
    """
739 915faf72 Renรฉ Nussbaumer
    query = []
740 915faf72 Renรฉ Nussbaumer
    body = {
741 915faf72 Renรฉ Nussbaumer
      "instances": instances,
742 915faf72 Renรฉ Nussbaumer
      }
743 915faf72 Renรฉ Nussbaumer
    self._UpdateWithKwargs(body, **kwargs)
744 915faf72 Renรฉ Nussbaumer
745 915faf72 Renรฉ Nussbaumer
    _AppendDryRunIf(query, kwargs.get("dry_run"))
746 915faf72 Renรฉ Nussbaumer
747 915faf72 Renรฉ Nussbaumer
    return self._SendRequest(HTTP_POST,
748 915faf72 Renรฉ Nussbaumer
                             "/%s/instances-multi-alloc" % GANETI_RAPI_VERSION,
749 915faf72 Renรฉ Nussbaumer
                             query, body)
750 915faf72 Renรฉ Nussbaumer
751 8a47b447 Michael Hanselmann
  def CreateInstance(self, mode, name, disk_template, disks, nics,
752 8a47b447 Michael Hanselmann
                     **kwargs):
753 95ab4de9 David Knowles
    """Creates a new instance.
754 95ab4de9 David Knowles

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

757 8a47b447 Michael Hanselmann
    @type mode: string
758 8a47b447 Michael Hanselmann
    @param mode: Instance creation mode
759 8a47b447 Michael Hanselmann
    @type name: string
760 8a47b447 Michael Hanselmann
    @param name: Hostname of the instance to create
761 8a47b447 Michael Hanselmann
    @type disk_template: string
762 8a47b447 Michael Hanselmann
    @param disk_template: Disk template for instance (e.g. plain, diskless,
763 8a47b447 Michael Hanselmann
                          file, or drbd)
764 8a47b447 Michael Hanselmann
    @type disks: list of dicts
765 8a47b447 Michael Hanselmann
    @param disks: List of disk definitions
766 8a47b447 Michael Hanselmann
    @type nics: list of dicts
767 8a47b447 Michael Hanselmann
    @param nics: List of NIC definitions
768 95ab4de9 David Knowles
    @type dry_run: bool
769 8a47b447 Michael Hanselmann
    @keyword dry_run: whether to perform a dry run
770 95ab4de9 David Knowles

771 98805538 Michael Hanselmann
    @rtype: string
772 95ab4de9 David Knowles
    @return: job id
773 95ab4de9 David Knowles

774 95ab4de9 David Knowles
    """
775 95ab4de9 David Knowles
    query = []
776 8a47b447 Michael Hanselmann
777 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, kwargs.get("dry_run"))
778 95ab4de9 David Knowles
779 8a47b447 Michael Hanselmann
    if _INST_CREATE_REQV1 in self.GetFeatures():
780 915faf72 Renรฉ Nussbaumer
      body = self.InstanceAllocation(mode, name, disk_template, disks, nics,
781 915faf72 Renรฉ Nussbaumer
                                     **kwargs)
782 915faf72 Renรฉ Nussbaumer
      body[_REQ_DATA_VERSION_FIELD] = 1
783 8a47b447 Michael Hanselmann
    else:
784 9a8ae794 Michael Hanselmann
      raise GanetiApiError("Server does not support new-style (version 1)"
785 9a8ae794 Michael Hanselmann
                           " instance creation requests")
786 8a47b447 Michael Hanselmann
787 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION,
788 8a47b447 Michael Hanselmann
                             query, body)
789 95ab4de9 David Knowles
790 95ab4de9 David Knowles
  def DeleteInstance(self, instance, dry_run=False):
791 95ab4de9 David Knowles
    """Deletes an instance.
792 95ab4de9 David Knowles

793 95ab4de9 David Knowles
    @type instance: str
794 95ab4de9 David Knowles
    @param instance: the instance to delete
795 95ab4de9 David Knowles

796 98805538 Michael Hanselmann
    @rtype: string
797 cab667cc David Knowles
    @return: job id
798 cab667cc David Knowles

799 95ab4de9 David Knowles
    """
800 95ab4de9 David Knowles
    query = []
801 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
802 95ab4de9 David Knowles
803 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
804 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s" %
805 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
806 95ab4de9 David Knowles
807 3b7158ef Michael Hanselmann
  def ModifyInstance(self, instance, **kwargs):
808 3b7158ef Michael Hanselmann
    """Modifies an instance.
809 3b7158ef Michael Hanselmann

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

812 3b7158ef Michael Hanselmann
    @type instance: string
813 3b7158ef Michael Hanselmann
    @param instance: Instance name
814 98805538 Michael Hanselmann
    @rtype: string
815 3b7158ef Michael Hanselmann
    @return: job id
816 3b7158ef Michael Hanselmann

817 3b7158ef Michael Hanselmann
    """
818 3b7158ef Michael Hanselmann
    body = kwargs
819 3b7158ef Michael Hanselmann
820 3b7158ef Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
821 3b7158ef Michael Hanselmann
                             ("/%s/instances/%s/modify" %
822 3b7158ef Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
823 3b7158ef Michael Hanselmann
824 b680c8be Michael Hanselmann
  def ActivateInstanceDisks(self, instance, ignore_size=None):
825 b680c8be Michael Hanselmann
    """Activates an instance's disks.
826 b680c8be Michael Hanselmann

827 b680c8be Michael Hanselmann
    @type instance: string
828 b680c8be Michael Hanselmann
    @param instance: Instance name
829 b680c8be Michael Hanselmann
    @type ignore_size: bool
830 b680c8be Michael Hanselmann
    @param ignore_size: Whether to ignore recorded size
831 d914c76f Simeon Miteff
    @rtype: string
832 b680c8be Michael Hanselmann
    @return: job id
833 b680c8be Michael Hanselmann

834 b680c8be Michael Hanselmann
    """
835 b680c8be Michael Hanselmann
    query = []
836 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_size, ("ignore_size", 1))
837 b680c8be Michael Hanselmann
838 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
839 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/activate-disks" %
840 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
841 b680c8be Michael Hanselmann
842 b680c8be Michael Hanselmann
  def DeactivateInstanceDisks(self, instance):
843 b680c8be Michael Hanselmann
    """Deactivates an instance's disks.
844 b680c8be Michael Hanselmann

845 b680c8be Michael Hanselmann
    @type instance: string
846 b680c8be Michael Hanselmann
    @param instance: Instance name
847 d914c76f Simeon Miteff
    @rtype: string
848 b680c8be Michael Hanselmann
    @return: job id
849 b680c8be Michael Hanselmann

850 b680c8be Michael Hanselmann
    """
851 b680c8be Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
852 b680c8be Michael Hanselmann
                             ("/%s/instances/%s/deactivate-disks" %
853 b680c8be Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
854 b680c8be Michael Hanselmann
855 a52978c7 Michael Hanselmann
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
856 a52978c7 Michael Hanselmann
    """Recreate an instance's disks.
857 a52978c7 Michael Hanselmann

858 a52978c7 Michael Hanselmann
    @type instance: string
859 a52978c7 Michael Hanselmann
    @param instance: Instance name
860 a52978c7 Michael Hanselmann
    @type disks: list of int
861 a52978c7 Michael Hanselmann
    @param disks: List of disk indexes
862 a52978c7 Michael Hanselmann
    @type nodes: list of string
863 a52978c7 Michael Hanselmann
    @param nodes: New instance nodes, if relocation is desired
864 a52978c7 Michael Hanselmann
    @rtype: string
865 a52978c7 Michael Hanselmann
    @return: job id
866 a52978c7 Michael Hanselmann

867 a52978c7 Michael Hanselmann
    """
868 a52978c7 Michael Hanselmann
    body = {}
869 57d8e228 Michael Hanselmann
    _SetItemIf(body, disks is not None, "disks", disks)
870 57d8e228 Michael Hanselmann
    _SetItemIf(body, nodes is not None, "nodes", nodes)
871 a52978c7 Michael Hanselmann
872 a52978c7 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
873 a52978c7 Michael Hanselmann
                             ("/%s/instances/%s/recreate-disks" %
874 a52978c7 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
875 a52978c7 Michael Hanselmann
876 e23881ed Michael Hanselmann
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
877 e23881ed Michael Hanselmann
    """Grows a disk of an instance.
878 e23881ed Michael Hanselmann

879 e23881ed Michael Hanselmann
    More details for parameters can be found in the RAPI documentation.
880 e23881ed Michael Hanselmann

881 e23881ed Michael Hanselmann
    @type instance: string
882 e23881ed Michael Hanselmann
    @param instance: Instance name
883 e23881ed Michael Hanselmann
    @type disk: integer
884 e23881ed Michael Hanselmann
    @param disk: Disk index
885 e23881ed Michael Hanselmann
    @type amount: integer
886 e23881ed Michael Hanselmann
    @param amount: Grow disk by this amount (MiB)
887 e23881ed Michael Hanselmann
    @type wait_for_sync: bool
888 e23881ed Michael Hanselmann
    @param wait_for_sync: Wait for disk to synchronize
889 98805538 Michael Hanselmann
    @rtype: string
890 e23881ed Michael Hanselmann
    @return: job id
891 e23881ed Michael Hanselmann

892 e23881ed Michael Hanselmann
    """
893 e23881ed Michael Hanselmann
    body = {
894 e23881ed Michael Hanselmann
      "amount": amount,
895 e23881ed Michael Hanselmann
      }
896 e23881ed Michael Hanselmann
897 57d8e228 Michael Hanselmann
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
898 e23881ed Michael Hanselmann
899 e23881ed Michael Hanselmann
    return self._SendRequest(HTTP_POST,
900 e23881ed Michael Hanselmann
                             ("/%s/instances/%s/disk/%s/grow" %
901 e23881ed Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance, disk)),
902 e23881ed Michael Hanselmann
                             None, body)
903 e23881ed Michael Hanselmann
904 95ab4de9 David Knowles
  def GetInstanceTags(self, instance):
905 95ab4de9 David Knowles
    """Gets tags for an instance.
906 95ab4de9 David Knowles

907 95ab4de9 David Knowles
    @type instance: str
908 95ab4de9 David Knowles
    @param instance: instance whose tags to return
909 95ab4de9 David Knowles

910 95ab4de9 David Knowles
    @rtype: list of str
911 95ab4de9 David Knowles
    @return: tags for the instance
912 95ab4de9 David Knowles

913 95ab4de9 David Knowles
    """
914 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
915 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
916 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
917 95ab4de9 David Knowles
918 95ab4de9 David Knowles
  def AddInstanceTags(self, instance, tags, dry_run=False):
919 95ab4de9 David Knowles
    """Adds tags to an instance.
920 95ab4de9 David Knowles

921 95ab4de9 David Knowles
    @type instance: str
922 95ab4de9 David Knowles
    @param instance: instance to add tags to
923 95ab4de9 David Knowles
    @type tags: list of str
924 95ab4de9 David Knowles
    @param tags: tags to add to the instance
925 95ab4de9 David Knowles
    @type dry_run: bool
926 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
927 95ab4de9 David Knowles

928 98805538 Michael Hanselmann
    @rtype: string
929 95ab4de9 David Knowles
    @return: job id
930 95ab4de9 David Knowles

931 95ab4de9 David Knowles
    """
932 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
933 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
934 95ab4de9 David Knowles
935 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
936 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
937 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
938 95ab4de9 David Knowles
939 95ab4de9 David Knowles
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
940 95ab4de9 David Knowles
    """Deletes tags from an instance.
941 95ab4de9 David Knowles

942 95ab4de9 David Knowles
    @type instance: str
943 95ab4de9 David Knowles
    @param instance: instance to delete tags from
944 95ab4de9 David Knowles
    @type tags: list of str
945 95ab4de9 David Knowles
    @param tags: tags to delete
946 95ab4de9 David Knowles
    @type dry_run: bool
947 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
948 d914c76f Simeon Miteff
    @rtype: string
949 d914c76f Simeon Miteff
    @return: job id
950 95ab4de9 David Knowles

951 95ab4de9 David Knowles
    """
952 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
953 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
954 95ab4de9 David Knowles
955 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
956 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/tags" %
957 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
958 95ab4de9 David Knowles
959 95ab4de9 David Knowles
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
960 95ab4de9 David Knowles
                     dry_run=False):
961 95ab4de9 David Knowles
    """Reboots an instance.
962 95ab4de9 David Knowles

963 95ab4de9 David Knowles
    @type instance: str
964 95ab4de9 David Knowles
    @param instance: instance to rebot
965 95ab4de9 David Knowles
    @type reboot_type: str
966 95ab4de9 David Knowles
    @param reboot_type: one of: hard, soft, full
967 95ab4de9 David Knowles
    @type ignore_secondaries: bool
968 95ab4de9 David Knowles
    @param ignore_secondaries: if True, ignores errors for the secondary node
969 95ab4de9 David Knowles
        while re-assembling disks (in hard-reboot mode only)
970 95ab4de9 David Knowles
    @type dry_run: bool
971 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
972 d914c76f Simeon Miteff
    @rtype: string
973 d914c76f Simeon Miteff
    @return: job id
974 95ab4de9 David Knowles

975 95ab4de9 David Knowles
    """
976 95ab4de9 David Knowles
    query = []
977 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
978 4c864b55 Michael Hanselmann
    _AppendIf(query, reboot_type, ("type", reboot_type))
979 4c864b55 Michael Hanselmann
    _AppendIf(query, ignore_secondaries is not None,
980 4c864b55 Michael Hanselmann
              ("ignore_secondaries", ignore_secondaries))
981 95ab4de9 David Knowles
982 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
983 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reboot" %
984 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
985 95ab4de9 David Knowles
986 b8481ebf Guido Trotter
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False,
987 b8481ebf Guido Trotter
                       **kwargs):
988 95ab4de9 David Knowles
    """Shuts down an instance.
989 95ab4de9 David Knowles

990 95ab4de9 David Knowles
    @type instance: str
991 95ab4de9 David Knowles
    @param instance: the instance to shut down
992 95ab4de9 David Knowles
    @type dry_run: bool
993 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
994 2ba39b8f Iustin Pop
    @type no_remember: bool
995 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
996 d914c76f Simeon Miteff
    @rtype: string
997 d914c76f Simeon Miteff
    @return: job id
998 95ab4de9 David Knowles

999 95ab4de9 David Knowles
    """
1000 95ab4de9 David Knowles
    query = []
1001 b8481ebf Guido Trotter
    body = kwargs
1002 b8481ebf Guido Trotter
1003 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1004 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
1005 95ab4de9 David Knowles
1006 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1007 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/shutdown" %
1008 b8481ebf Guido Trotter
                              (GANETI_RAPI_VERSION, instance)), query, body)
1009 95ab4de9 David Knowles
1010 2ba39b8f Iustin Pop
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
1011 95ab4de9 David Knowles
    """Starts up an instance.
1012 95ab4de9 David Knowles

1013 95ab4de9 David Knowles
    @type instance: str
1014 95ab4de9 David Knowles
    @param instance: the instance to start up
1015 95ab4de9 David Knowles
    @type dry_run: bool
1016 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1017 2ba39b8f Iustin Pop
    @type no_remember: bool
1018 2ba39b8f Iustin Pop
    @param no_remember: if true, will not record the state change
1019 d914c76f Simeon Miteff
    @rtype: string
1020 d914c76f Simeon Miteff
    @return: job id
1021 95ab4de9 David Knowles

1022 95ab4de9 David Knowles
    """
1023 95ab4de9 David Knowles
    query = []
1024 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1025 4c864b55 Michael Hanselmann
    _AppendIf(query, no_remember, ("no-remember", 1))
1026 95ab4de9 David Knowles
1027 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1028 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/startup" %
1029 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1030 95ab4de9 David Knowles
1031 c744425f Michael Hanselmann
  def ReinstallInstance(self, instance, os=None, no_startup=False,
1032 c744425f Michael Hanselmann
                        osparams=None):
1033 95ab4de9 David Knowles
    """Reinstalls an instance.
1034 95ab4de9 David Knowles

1035 95ab4de9 David Knowles
    @type instance: str
1036 fcee9675 David Knowles
    @param instance: The instance to reinstall
1037 fcee9675 David Knowles
    @type os: str or None
1038 fcee9675 David Knowles
    @param os: The operating system to reinstall. If None, the instance's
1039 fcee9675 David Knowles
        current operating system will be installed again
1040 95ab4de9 David Knowles
    @type no_startup: bool
1041 fcee9675 David Knowles
    @param no_startup: Whether to start the instance automatically
1042 d914c76f Simeon Miteff
    @rtype: string
1043 d914c76f Simeon Miteff
    @return: job id
1044 95ab4de9 David Knowles

1045 95ab4de9 David Knowles
    """
1046 c744425f Michael Hanselmann
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
1047 c744425f Michael Hanselmann
      body = {
1048 c744425f Michael Hanselmann
        "start": not no_startup,
1049 c744425f Michael Hanselmann
        }
1050 57d8e228 Michael Hanselmann
      _SetItemIf(body, os is not None, "os", os)
1051 57d8e228 Michael Hanselmann
      _SetItemIf(body, osparams is not None, "osparams", osparams)
1052 c744425f Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1053 c744425f Michael Hanselmann
                               ("/%s/instances/%s/reinstall" %
1054 c744425f Michael Hanselmann
                                (GANETI_RAPI_VERSION, instance)), None, body)
1055 c744425f Michael Hanselmann
1056 c744425f Michael Hanselmann
    # Use old request format
1057 c744425f Michael Hanselmann
    if osparams:
1058 c744425f Michael Hanselmann
      raise GanetiApiError("Server does not support specifying OS parameters"
1059 c744425f Michael Hanselmann
                           " for instance reinstallation")
1060 c744425f Michael Hanselmann
1061 fcee9675 David Knowles
    query = []
1062 4c864b55 Michael Hanselmann
    _AppendIf(query, os, ("os", os))
1063 4c864b55 Michael Hanselmann
    _AppendIf(query, no_startup, ("nostartup", 1))
1064 4c864b55 Michael Hanselmann
1065 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1066 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/reinstall" %
1067 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1068 95ab4de9 David Knowles
1069 bfc2002f Michael Hanselmann
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1070 539d65ba Michael Hanselmann
                           remote_node=None, iallocator=None):
1071 95ab4de9 David Knowles
    """Replaces disks on an instance.
1072 95ab4de9 David Knowles

1073 95ab4de9 David Knowles
    @type instance: str
1074 95ab4de9 David Knowles
    @param instance: instance whose disks to replace
1075 bfc2002f Michael Hanselmann
    @type disks: list of ints
1076 bfc2002f Michael Hanselmann
    @param disks: Indexes of disks to replace
1077 95ab4de9 David Knowles
    @type mode: str
1078 cfc03c54 Michael Hanselmann
    @param mode: replacement mode to use (defaults to replace_auto)
1079 95ab4de9 David Knowles
    @type remote_node: str or None
1080 95ab4de9 David Knowles
    @param remote_node: new secondary node to use (for use with
1081 cfc03c54 Michael Hanselmann
        replace_new_secondary mode)
1082 95ab4de9 David Knowles
    @type iallocator: str or None
1083 95ab4de9 David Knowles
    @param iallocator: instance allocator plugin to use (for use with
1084 cfc03c54 Michael Hanselmann
                       replace_auto mode)
1085 95ab4de9 David Knowles

1086 98805538 Michael Hanselmann
    @rtype: string
1087 95ab4de9 David Knowles
    @return: job id
1088 95ab4de9 David Knowles

1089 95ab4de9 David Knowles
    """
1090 cfc03c54 Michael Hanselmann
    query = [
1091 cfc03c54 Michael Hanselmann
      ("mode", mode),
1092 cfc03c54 Michael Hanselmann
      ]
1093 95ab4de9 David Knowles
1094 539d65ba Michael Hanselmann
    # TODO: Convert to body parameters
1095 539d65ba Michael Hanselmann
1096 539d65ba Michael Hanselmann
    if disks is not None:
1097 4c864b55 Michael Hanselmann
      _AppendIf(query, True,
1098 4c864b55 Michael Hanselmann
                ("disks", ",".join(str(idx) for idx in disks)))
1099 bfc2002f Michael Hanselmann
1100 4c864b55 Michael Hanselmann
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1101 4c864b55 Michael Hanselmann
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1102 bfc2002f Michael Hanselmann
1103 95ab4de9 David Knowles
    return self._SendRequest(HTTP_POST,
1104 a198b2d9 Michael Hanselmann
                             ("/%s/instances/%s/replace-disks" %
1105 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1106 95ab4de9 David Knowles
1107 ebeb600f Michael Hanselmann
  def PrepareExport(self, instance, mode):
1108 ebeb600f Michael Hanselmann
    """Prepares an instance for an export.
1109 ebeb600f Michael Hanselmann

1110 ebeb600f Michael Hanselmann
    @type instance: string
1111 ebeb600f Michael Hanselmann
    @param instance: Instance name
1112 ebeb600f Michael Hanselmann
    @type mode: string
1113 ebeb600f Michael Hanselmann
    @param mode: Export mode
1114 ebeb600f Michael Hanselmann
    @rtype: string
1115 ebeb600f Michael Hanselmann
    @return: Job ID
1116 ebeb600f Michael Hanselmann

1117 ebeb600f Michael Hanselmann
    """
1118 ebeb600f Michael Hanselmann
    query = [("mode", mode)]
1119 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1120 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/prepare-export" %
1121 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), query, None)
1122 ebeb600f Michael Hanselmann
1123 ebeb600f Michael Hanselmann
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1124 ebeb600f Michael Hanselmann
                     remove_instance=None,
1125 ebeb600f Michael Hanselmann
                     x509_key_name=None, destination_x509_ca=None):
1126 ebeb600f Michael Hanselmann
    """Exports an instance.
1127 ebeb600f Michael Hanselmann

1128 ebeb600f Michael Hanselmann
    @type instance: string
1129 ebeb600f Michael Hanselmann
    @param instance: Instance name
1130 ebeb600f Michael Hanselmann
    @type mode: string
1131 ebeb600f Michael Hanselmann
    @param mode: Export mode
1132 ebeb600f Michael Hanselmann
    @rtype: string
1133 ebeb600f Michael Hanselmann
    @return: Job ID
1134 ebeb600f Michael Hanselmann

1135 ebeb600f Michael Hanselmann
    """
1136 ebeb600f Michael Hanselmann
    body = {
1137 ebeb600f Michael Hanselmann
      "destination": destination,
1138 ebeb600f Michael Hanselmann
      "mode": mode,
1139 ebeb600f Michael Hanselmann
      }
1140 ebeb600f Michael Hanselmann
1141 57d8e228 Michael Hanselmann
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1142 57d8e228 Michael Hanselmann
    _SetItemIf(body, remove_instance is not None,
1143 57d8e228 Michael Hanselmann
               "remove_instance", remove_instance)
1144 57d8e228 Michael Hanselmann
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1145 57d8e228 Michael Hanselmann
    _SetItemIf(body, destination_x509_ca is not None,
1146 57d8e228 Michael Hanselmann
               "destination_x509_ca", destination_x509_ca)
1147 ebeb600f Michael Hanselmann
1148 ebeb600f Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1149 ebeb600f Michael Hanselmann
                             ("/%s/instances/%s/export" %
1150 ebeb600f Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1151 ebeb600f Michael Hanselmann
1152 e0ac6ce6 Michael Hanselmann
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1153 c63eb9c0 Michael Hanselmann
    """Migrates an instance.
1154 e0ac6ce6 Michael Hanselmann

1155 e0ac6ce6 Michael Hanselmann
    @type instance: string
1156 e0ac6ce6 Michael Hanselmann
    @param instance: Instance name
1157 e0ac6ce6 Michael Hanselmann
    @type mode: string
1158 e0ac6ce6 Michael Hanselmann
    @param mode: Migration mode
1159 e0ac6ce6 Michael Hanselmann
    @type cleanup: bool
1160 e0ac6ce6 Michael Hanselmann
    @param cleanup: Whether to clean up a previously failed migration
1161 d914c76f Simeon Miteff
    @rtype: string
1162 d914c76f Simeon Miteff
    @return: job id
1163 e0ac6ce6 Michael Hanselmann

1164 e0ac6ce6 Michael Hanselmann
    """
1165 e0ac6ce6 Michael Hanselmann
    body = {}
1166 57d8e228 Michael Hanselmann
    _SetItemIf(body, mode is not None, "mode", mode)
1167 57d8e228 Michael Hanselmann
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1168 e0ac6ce6 Michael Hanselmann
1169 e0ac6ce6 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1170 e0ac6ce6 Michael Hanselmann
                             ("/%s/instances/%s/migrate" %
1171 e0ac6ce6 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1172 e0ac6ce6 Michael Hanselmann
1173 c0a146a1 Michael Hanselmann
  def FailoverInstance(self, instance, iallocator=None,
1174 c0a146a1 Michael Hanselmann
                       ignore_consistency=None, target_node=None):
1175 c0a146a1 Michael Hanselmann
    """Does a failover of an instance.
1176 c0a146a1 Michael Hanselmann

1177 c0a146a1 Michael Hanselmann
    @type instance: string
1178 c0a146a1 Michael Hanselmann
    @param instance: Instance name
1179 c0a146a1 Michael Hanselmann
    @type iallocator: string
1180 c0a146a1 Michael Hanselmann
    @param iallocator: Iallocator for deciding the target node for
1181 c0a146a1 Michael Hanselmann
      shared-storage instances
1182 c0a146a1 Michael Hanselmann
    @type ignore_consistency: bool
1183 c0a146a1 Michael Hanselmann
    @param ignore_consistency: Whether to ignore disk consistency
1184 c0a146a1 Michael Hanselmann
    @type target_node: string
1185 c0a146a1 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1186 c0a146a1 Michael Hanselmann
    @rtype: string
1187 c0a146a1 Michael Hanselmann
    @return: job id
1188 c0a146a1 Michael Hanselmann

1189 c0a146a1 Michael Hanselmann
    """
1190 c0a146a1 Michael Hanselmann
    body = {}
1191 57d8e228 Michael Hanselmann
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1192 57d8e228 Michael Hanselmann
    _SetItemIf(body, ignore_consistency is not None,
1193 57d8e228 Michael Hanselmann
               "ignore_consistency", ignore_consistency)
1194 57d8e228 Michael Hanselmann
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1195 c0a146a1 Michael Hanselmann
1196 c0a146a1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1197 c0a146a1 Michael Hanselmann
                             ("/%s/instances/%s/failover" %
1198 c0a146a1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1199 c0a146a1 Michael Hanselmann
1200 d654aae1 Michael Hanselmann
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1201 d654aae1 Michael Hanselmann
    """Changes the name of an instance.
1202 d654aae1 Michael Hanselmann

1203 d654aae1 Michael Hanselmann
    @type instance: string
1204 d654aae1 Michael Hanselmann
    @param instance: Instance name
1205 d654aae1 Michael Hanselmann
    @type new_name: string
1206 d654aae1 Michael Hanselmann
    @param new_name: New instance name
1207 d654aae1 Michael Hanselmann
    @type ip_check: bool
1208 d654aae1 Michael Hanselmann
    @param ip_check: Whether to ensure instance's IP address is inactive
1209 d654aae1 Michael Hanselmann
    @type name_check: bool
1210 d654aae1 Michael Hanselmann
    @param name_check: Whether to ensure instance's name is resolvable
1211 d914c76f Simeon Miteff
    @rtype: string
1212 d914c76f Simeon Miteff
    @return: job id
1213 d654aae1 Michael Hanselmann

1214 d654aae1 Michael Hanselmann
    """
1215 d654aae1 Michael Hanselmann
    body = {
1216 d654aae1 Michael Hanselmann
      "new_name": new_name,
1217 d654aae1 Michael Hanselmann
      }
1218 d654aae1 Michael Hanselmann
1219 57d8e228 Michael Hanselmann
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1220 57d8e228 Michael Hanselmann
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1221 d654aae1 Michael Hanselmann
1222 d654aae1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1223 d654aae1 Michael Hanselmann
                             ("/%s/instances/%s/rename" %
1224 d654aae1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, body)
1225 d654aae1 Michael Hanselmann
1226 b82d4c5e Michael Hanselmann
  def GetInstanceConsole(self, instance):
1227 b82d4c5e Michael Hanselmann
    """Request information for connecting to instance's console.
1228 b82d4c5e Michael Hanselmann

1229 b82d4c5e Michael Hanselmann
    @type instance: string
1230 b82d4c5e Michael Hanselmann
    @param instance: Instance name
1231 d914c76f Simeon Miteff
    @rtype: dict
1232 d914c76f Simeon Miteff
    @return: dictionary containing information about instance's console
1233 b82d4c5e Michael Hanselmann

1234 b82d4c5e Michael Hanselmann
    """
1235 b82d4c5e Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1236 b82d4c5e Michael Hanselmann
                             ("/%s/instances/%s/console" %
1237 b82d4c5e Michael Hanselmann
                              (GANETI_RAPI_VERSION, instance)), None, None)
1238 b82d4c5e Michael Hanselmann
1239 95ab4de9 David Knowles
  def GetJobs(self):
1240 95ab4de9 David Knowles
    """Gets all jobs for the cluster.
1241 95ab4de9 David Knowles

1242 95ab4de9 David Knowles
    @rtype: list of int
1243 95ab4de9 David Knowles
    @return: job ids for the cluster
1244 95ab4de9 David Knowles

1245 95ab4de9 David Knowles
    """
1246 768747ed Michael Hanselmann
    return [int(j["id"])
1247 a198b2d9 Michael Hanselmann
            for j in self._SendRequest(HTTP_GET,
1248 a198b2d9 Michael Hanselmann
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1249 a198b2d9 Michael Hanselmann
                                       None, None)]
1250 95ab4de9 David Knowles
1251 95ab4de9 David Knowles
  def GetJobStatus(self, job_id):
1252 95ab4de9 David Knowles
    """Gets the status of a job.
1253 95ab4de9 David Knowles

1254 98805538 Michael Hanselmann
    @type job_id: string
1255 95ab4de9 David Knowles
    @param job_id: job id whose status to query
1256 95ab4de9 David Knowles

1257 95ab4de9 David Knowles
    @rtype: dict
1258 95ab4de9 David Knowles
    @return: job status
1259 95ab4de9 David Knowles

1260 95ab4de9 David Knowles
    """
1261 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1262 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1263 a198b2d9 Michael Hanselmann
                             None, None)
1264 95ab4de9 David Knowles
1265 16c13387 Theo Van Dinter
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1266 16c13387 Theo Van Dinter
    """Polls cluster for job status until completion.
1267 16c13387 Theo Van Dinter

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

1271 cfda0e48 Iustin Pop
    @type job_id: string
1272 16c13387 Theo Van Dinter
    @param job_id: job id to watch
1273 16c13387 Theo Van Dinter
    @type period: int
1274 16c13387 Theo Van Dinter
    @param period: how often to poll for status (optional, default 5s)
1275 16c13387 Theo Van Dinter
    @type retries: int
1276 16c13387 Theo Van Dinter
    @param retries: how many time to poll before giving up
1277 16c13387 Theo Van Dinter
                    (optional, default -1 means unlimited)
1278 16c13387 Theo Van Dinter

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

1285 16c13387 Theo Van Dinter
    """
1286 16c13387 Theo Van Dinter
    while retries != 0:
1287 16c13387 Theo Van Dinter
      job_result = self.GetJobStatus(job_id)
1288 dde0e97c Michael Hanselmann
1289 dde0e97c Michael Hanselmann
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1290 16c13387 Theo Van Dinter
        return True
1291 dde0e97c Michael Hanselmann
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1292 dde0e97c Michael Hanselmann
        return False
1293 dde0e97c Michael Hanselmann
1294 dde0e97c Michael Hanselmann
      if period:
1295 dde0e97c Michael Hanselmann
        time.sleep(period)
1296 dde0e97c Michael Hanselmann
1297 16c13387 Theo Van Dinter
      if retries > 0:
1298 16c13387 Theo Van Dinter
        retries -= 1
1299 dde0e97c Michael Hanselmann
1300 16c13387 Theo Van Dinter
    return False
1301 16c13387 Theo Van Dinter
1302 d9b67f70 Michael Hanselmann
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1303 d9b67f70 Michael Hanselmann
    """Waits for job changes.
1304 d9b67f70 Michael Hanselmann

1305 98805538 Michael Hanselmann
    @type job_id: string
1306 d9b67f70 Michael Hanselmann
    @param job_id: Job ID for which to wait
1307 d914c76f Simeon Miteff
    @return: C{None} if no changes have been detected and a dict with two keys,
1308 d914c76f Simeon Miteff
      C{job_info} and C{log_entries} otherwise.
1309 d914c76f Simeon Miteff
    @rtype: dict
1310 d9b67f70 Michael Hanselmann

1311 d9b67f70 Michael Hanselmann
    """
1312 d9b67f70 Michael Hanselmann
    body = {
1313 d9b67f70 Michael Hanselmann
      "fields": fields,
1314 d9b67f70 Michael Hanselmann
      "previous_job_info": prev_job_info,
1315 d9b67f70 Michael Hanselmann
      "previous_log_serial": prev_log_serial,
1316 d9b67f70 Michael Hanselmann
      }
1317 d9b67f70 Michael Hanselmann
1318 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1319 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1320 a198b2d9 Michael Hanselmann
                             None, body)
1321 d9b67f70 Michael Hanselmann
1322 cf9ada49 Michael Hanselmann
  def CancelJob(self, job_id, dry_run=False):
1323 cf9ada49 Michael Hanselmann
    """Cancels a job.
1324 95ab4de9 David Knowles

1325 98805538 Michael Hanselmann
    @type job_id: string
1326 95ab4de9 David Knowles
    @param job_id: id of the job to delete
1327 95ab4de9 David Knowles
    @type dry_run: bool
1328 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1329 d914c76f Simeon Miteff
    @rtype: tuple
1330 d914c76f Simeon Miteff
    @return: tuple containing the result, and a message (bool, string)
1331 95ab4de9 David Knowles

1332 95ab4de9 David Knowles
    """
1333 95ab4de9 David Knowles
    query = []
1334 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1335 95ab4de9 David Knowles
1336 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1337 a198b2d9 Michael Hanselmann
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1338 a198b2d9 Michael Hanselmann
                             query, None)
1339 95ab4de9 David Knowles
1340 95ab4de9 David Knowles
  def GetNodes(self, bulk=False):
1341 95ab4de9 David Knowles
    """Gets all nodes in the cluster.
1342 95ab4de9 David Knowles

1343 95ab4de9 David Knowles
    @type bulk: bool
1344 95ab4de9 David Knowles
    @param bulk: whether to return all information about all instances
1345 95ab4de9 David Knowles

1346 95ab4de9 David Knowles
    @rtype: list of dict or str
1347 95ab4de9 David Knowles
    @return: if bulk is true, info about nodes in the cluster,
1348 95ab4de9 David Knowles
        else list of nodes in the cluster
1349 95ab4de9 David Knowles

1350 95ab4de9 David Knowles
    """
1351 95ab4de9 David Knowles
    query = []
1352 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1353 95ab4de9 David Knowles
1354 a198b2d9 Michael Hanselmann
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1355 a198b2d9 Michael Hanselmann
                              query, None)
1356 95ab4de9 David Knowles
    if bulk:
1357 95ab4de9 David Knowles
      return nodes
1358 95ab4de9 David Knowles
    else:
1359 95ab4de9 David Knowles
      return [n["id"] for n in nodes]
1360 95ab4de9 David Knowles
1361 591e5103 Michael Hanselmann
  def GetNode(self, node):
1362 95ab4de9 David Knowles
    """Gets information about a node.
1363 95ab4de9 David Knowles

1364 95ab4de9 David Knowles
    @type node: str
1365 95ab4de9 David Knowles
    @param node: node whose info to return
1366 95ab4de9 David Knowles

1367 95ab4de9 David Knowles
    @rtype: dict
1368 95ab4de9 David Knowles
    @return: info about the node
1369 95ab4de9 David Knowles

1370 95ab4de9 David Knowles
    """
1371 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1372 a198b2d9 Michael Hanselmann
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1373 a198b2d9 Michael Hanselmann
                             None, None)
1374 95ab4de9 David Knowles
1375 95ab4de9 David Knowles
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1376 de40437a Michael Hanselmann
                   dry_run=False, early_release=None,
1377 0b58db81 Michael Hanselmann
                   mode=None, accept_old=False):
1378 95ab4de9 David Knowles
    """Evacuates instances from a Ganeti node.
1379 95ab4de9 David Knowles

1380 95ab4de9 David Knowles
    @type node: str
1381 95ab4de9 David Knowles
    @param node: node to evacuate
1382 95ab4de9 David Knowles
    @type iallocator: str or None
1383 95ab4de9 David Knowles
    @param iallocator: instance allocator to use
1384 95ab4de9 David Knowles
    @type remote_node: str
1385 95ab4de9 David Knowles
    @param remote_node: node to evaucate to
1386 95ab4de9 David Knowles
    @type dry_run: bool
1387 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1388 941b9309 Iustin Pop
    @type early_release: bool
1389 941b9309 Iustin Pop
    @param early_release: whether to enable parallelization
1390 0b58db81 Michael Hanselmann
    @type mode: string
1391 0b58db81 Michael Hanselmann
    @param mode: Node evacuation mode
1392 de40437a Michael Hanselmann
    @type accept_old: bool
1393 de40437a Michael Hanselmann
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1394 de40437a Michael Hanselmann
        results
1395 de40437a Michael Hanselmann

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

1402 941b9309 Iustin Pop
    @raises GanetiApiError: if an iallocator and remote_node are both
1403 941b9309 Iustin Pop
        specified
1404 95ab4de9 David Knowles

1405 95ab4de9 David Knowles
    """
1406 95ab4de9 David Knowles
    if iallocator and remote_node:
1407 cfc03c54 Michael Hanselmann
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1408 95ab4de9 David Knowles
1409 cfc03c54 Michael Hanselmann
    query = []
1410 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1411 de40437a Michael Hanselmann
1412 de40437a Michael Hanselmann
    if _NODE_EVAC_RES1 in self.GetFeatures():
1413 0b58db81 Michael Hanselmann
      # Server supports body parameters
1414 de40437a Michael Hanselmann
      body = {}
1415 de40437a Michael Hanselmann
1416 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1417 57d8e228 Michael Hanselmann
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1418 57d8e228 Michael Hanselmann
      _SetItemIf(body, early_release is not None,
1419 57d8e228 Michael Hanselmann
                 "early_release", early_release)
1420 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1421 de40437a Michael Hanselmann
    else:
1422 de40437a Michael Hanselmann
      # Pre-2.5 request format
1423 de40437a Michael Hanselmann
      body = None
1424 de40437a Michael Hanselmann
1425 de40437a Michael Hanselmann
      if not accept_old:
1426 de40437a Michael Hanselmann
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1427 de40437a Michael Hanselmann
                             " not accept old-style results (parameter"
1428 de40437a Michael Hanselmann
                             " accept_old)")
1429 de40437a Michael Hanselmann
1430 0b58db81 Michael Hanselmann
      # Pre-2.5 servers can only evacuate secondaries
1431 0b58db81 Michael Hanselmann
      if mode is not None and mode != NODE_EVAC_SEC:
1432 de40437a Michael Hanselmann
        raise GanetiApiError("Server can only evacuate secondary instances")
1433 de40437a Michael Hanselmann
1434 4c864b55 Michael Hanselmann
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1435 4c864b55 Michael Hanselmann
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1436 4c864b55 Michael Hanselmann
      _AppendIf(query, early_release, ("early_release", 1))
1437 95ab4de9 David Knowles
1438 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1439 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/evacuate" %
1440 de40437a Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, body)
1441 95ab4de9 David Knowles
1442 b7a1c816 Michael Hanselmann
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1443 b7a1c816 Michael Hanselmann
                  target_node=None):
1444 95ab4de9 David Knowles
    """Migrates all primary instances from a node.
1445 95ab4de9 David Knowles

1446 95ab4de9 David Knowles
    @type node: str
1447 95ab4de9 David Knowles
    @param node: node to migrate
1448 1f334d96 Iustin Pop
    @type mode: string
1449 1f334d96 Iustin Pop
    @param mode: if passed, it will overwrite the live migration type,
1450 1f334d96 Iustin Pop
        otherwise the hypervisor default will be used
1451 95ab4de9 David Knowles
    @type dry_run: bool
1452 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1453 b7a1c816 Michael Hanselmann
    @type iallocator: string
1454 b7a1c816 Michael Hanselmann
    @param iallocator: instance allocator to use
1455 b7a1c816 Michael Hanselmann
    @type target_node: string
1456 b7a1c816 Michael Hanselmann
    @param target_node: Target node for shared-storage instances
1457 95ab4de9 David Knowles

1458 98805538 Michael Hanselmann
    @rtype: string
1459 95ab4de9 David Knowles
    @return: job id
1460 95ab4de9 David Knowles

1461 95ab4de9 David Knowles
    """
1462 95ab4de9 David Knowles
    query = []
1463 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1464 95ab4de9 David Knowles
1465 b7a1c816 Michael Hanselmann
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1466 b7a1c816 Michael Hanselmann
      body = {}
1467 b7a1c816 Michael Hanselmann
1468 57d8e228 Michael Hanselmann
      _SetItemIf(body, mode is not None, "mode", mode)
1469 57d8e228 Michael Hanselmann
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1470 57d8e228 Michael Hanselmann
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1471 b7a1c816 Michael Hanselmann
1472 b7a1c816 Michael Hanselmann
      assert len(query) <= 1
1473 b7a1c816 Michael Hanselmann
1474 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1475 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1476 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, body)
1477 b7a1c816 Michael Hanselmann
    else:
1478 b7a1c816 Michael Hanselmann
      # Use old request format
1479 b7a1c816 Michael Hanselmann
      if target_node is not None:
1480 b7a1c816 Michael Hanselmann
        raise GanetiApiError("Server does not support specifying target node"
1481 b7a1c816 Michael Hanselmann
                             " for node migration")
1482 b7a1c816 Michael Hanselmann
1483 4c864b55 Michael Hanselmann
      _AppendIf(query, mode is not None, ("mode", mode))
1484 b7a1c816 Michael Hanselmann
1485 b7a1c816 Michael Hanselmann
      return self._SendRequest(HTTP_POST,
1486 b7a1c816 Michael Hanselmann
                               ("/%s/nodes/%s/migrate" %
1487 b7a1c816 Michael Hanselmann
                                (GANETI_RAPI_VERSION, node)), query, None)
1488 95ab4de9 David Knowles
1489 95ab4de9 David Knowles
  def GetNodeRole(self, node):
1490 95ab4de9 David Knowles
    """Gets the current role for a node.
1491 95ab4de9 David Knowles

1492 95ab4de9 David Knowles
    @type node: str
1493 95ab4de9 David Knowles
    @param node: node whose role to return
1494 95ab4de9 David Knowles

1495 95ab4de9 David Knowles
    @rtype: str
1496 95ab4de9 David Knowles
    @return: the current role for a node
1497 95ab4de9 David Knowles

1498 95ab4de9 David Knowles
    """
1499 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1500 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1501 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1502 95ab4de9 David Knowles
1503 8de8e68d Michael Hanselmann
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1504 95ab4de9 David Knowles
    """Sets the role for a node.
1505 95ab4de9 David Knowles

1506 95ab4de9 David Knowles
    @type node: str
1507 95ab4de9 David Knowles
    @param node: the node whose role to set
1508 95ab4de9 David Knowles
    @type role: str
1509 95ab4de9 David Knowles
    @param role: the role to set for the node
1510 95ab4de9 David Knowles
    @type force: bool
1511 95ab4de9 David Knowles
    @param force: whether to force the role change
1512 8de8e68d Michael Hanselmann
    @type auto_promote: bool
1513 8de8e68d Michael Hanselmann
    @param auto_promote: Whether node(s) should be promoted to master candidate
1514 8de8e68d Michael Hanselmann
                         if necessary
1515 95ab4de9 David Knowles

1516 98805538 Michael Hanselmann
    @rtype: string
1517 95ab4de9 David Knowles
    @return: job id
1518 95ab4de9 David Knowles

1519 95ab4de9 David Knowles
    """
1520 4c864b55 Michael Hanselmann
    query = []
1521 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1522 4c864b55 Michael Hanselmann
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1523 8de8e68d Michael Hanselmann
1524 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1525 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/role" %
1526 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, role)
1527 95ab4de9 David Knowles
1528 42d4d8b9 Michael Hanselmann
  def PowercycleNode(self, node, force=False):
1529 42d4d8b9 Michael Hanselmann
    """Powercycles a node.
1530 42d4d8b9 Michael Hanselmann

1531 42d4d8b9 Michael Hanselmann
    @type node: string
1532 42d4d8b9 Michael Hanselmann
    @param node: Node name
1533 42d4d8b9 Michael Hanselmann
    @type force: bool
1534 42d4d8b9 Michael Hanselmann
    @param force: Whether to force the operation
1535 42d4d8b9 Michael Hanselmann
    @rtype: string
1536 42d4d8b9 Michael Hanselmann
    @return: job id
1537 42d4d8b9 Michael Hanselmann

1538 42d4d8b9 Michael Hanselmann
    """
1539 4c864b55 Michael Hanselmann
    query = []
1540 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
1541 42d4d8b9 Michael Hanselmann
1542 42d4d8b9 Michael Hanselmann
    return self._SendRequest(HTTP_POST,
1543 42d4d8b9 Michael Hanselmann
                             ("/%s/nodes/%s/powercycle" %
1544 42d4d8b9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1545 42d4d8b9 Michael Hanselmann
1546 370f2042 Guido Trotter
  def ModifyNode(self, node, **kwargs):
1547 94497dd1 Michael Hanselmann
    """Modifies a node.
1548 94497dd1 Michael Hanselmann

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

1551 370f2042 Guido Trotter
    @type node: string
1552 370f2042 Guido Trotter
    @param node: Node name
1553 94497dd1 Michael Hanselmann
    @rtype: string
1554 94497dd1 Michael Hanselmann
    @return: job id
1555 94497dd1 Michael Hanselmann

1556 94497dd1 Michael Hanselmann
    """
1557 e366273a Guido Trotter
    return self._SendRequest(HTTP_POST,
1558 94497dd1 Michael Hanselmann
                             ("/%s/nodes/%s/modify" %
1559 370f2042 Guido Trotter
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1560 94497dd1 Michael Hanselmann
1561 95ab4de9 David Knowles
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1562 95ab4de9 David Knowles
    """Gets the storage units for a node.
1563 95ab4de9 David Knowles

1564 95ab4de9 David Knowles
    @type node: str
1565 95ab4de9 David Knowles
    @param node: the node whose storage units to return
1566 95ab4de9 David Knowles
    @type storage_type: str
1567 95ab4de9 David Knowles
    @param storage_type: storage type whose units to return
1568 95ab4de9 David Knowles
    @type output_fields: str
1569 95ab4de9 David Knowles
    @param output_fields: storage type fields to return
1570 95ab4de9 David Knowles

1571 98805538 Michael Hanselmann
    @rtype: string
1572 95ab4de9 David Knowles
    @return: job id where results can be retrieved
1573 95ab4de9 David Knowles

1574 95ab4de9 David Knowles
    """
1575 cfc03c54 Michael Hanselmann
    query = [
1576 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1577 cfc03c54 Michael Hanselmann
      ("output_fields", output_fields),
1578 cfc03c54 Michael Hanselmann
      ]
1579 95ab4de9 David Knowles
1580 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1581 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage" %
1582 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1583 95ab4de9 David Knowles
1584 fde28316 Michael Hanselmann
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1585 95ab4de9 David Knowles
    """Modifies parameters of storage units on the node.
1586 95ab4de9 David Knowles

1587 95ab4de9 David Knowles
    @type node: str
1588 95ab4de9 David Knowles
    @param node: node whose storage units to modify
1589 95ab4de9 David Knowles
    @type storage_type: str
1590 95ab4de9 David Knowles
    @param storage_type: storage type whose units to modify
1591 95ab4de9 David Knowles
    @type name: str
1592 95ab4de9 David Knowles
    @param name: name of the storage unit
1593 fde28316 Michael Hanselmann
    @type allocatable: bool or None
1594 fde28316 Michael Hanselmann
    @param allocatable: Whether to set the "allocatable" flag on the storage
1595 fde28316 Michael Hanselmann
                        unit (None=no modification, True=set, False=unset)
1596 95ab4de9 David Knowles

1597 98805538 Michael Hanselmann
    @rtype: string
1598 95ab4de9 David Knowles
    @return: job id
1599 95ab4de9 David Knowles

1600 95ab4de9 David Knowles
    """
1601 95ab4de9 David Knowles
    query = [
1602 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1603 cfc03c54 Michael Hanselmann
      ("name", name),
1604 cfc03c54 Michael Hanselmann
      ]
1605 cfc03c54 Michael Hanselmann
1606 4c864b55 Michael Hanselmann
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1607 fde28316 Michael Hanselmann
1608 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1609 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/modify" %
1610 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1611 95ab4de9 David Knowles
1612 95ab4de9 David Knowles
  def RepairNodeStorageUnits(self, node, storage_type, name):
1613 95ab4de9 David Knowles
    """Repairs a storage unit on the node.
1614 95ab4de9 David Knowles

1615 95ab4de9 David Knowles
    @type node: str
1616 95ab4de9 David Knowles
    @param node: node whose storage units to repair
1617 95ab4de9 David Knowles
    @type storage_type: str
1618 95ab4de9 David Knowles
    @param storage_type: storage type to repair
1619 95ab4de9 David Knowles
    @type name: str
1620 95ab4de9 David Knowles
    @param name: name of the storage unit to repair
1621 95ab4de9 David Knowles

1622 98805538 Michael Hanselmann
    @rtype: string
1623 95ab4de9 David Knowles
    @return: job id
1624 95ab4de9 David Knowles

1625 95ab4de9 David Knowles
    """
1626 cfc03c54 Michael Hanselmann
    query = [
1627 cfc03c54 Michael Hanselmann
      ("storage_type", storage_type),
1628 cfc03c54 Michael Hanselmann
      ("name", name),
1629 cfc03c54 Michael Hanselmann
      ]
1630 95ab4de9 David Knowles
1631 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1632 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/storage/repair" %
1633 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1634 95ab4de9 David Knowles
1635 95ab4de9 David Knowles
  def GetNodeTags(self, node):
1636 95ab4de9 David Knowles
    """Gets the tags for a node.
1637 95ab4de9 David Knowles

1638 95ab4de9 David Knowles
    @type node: str
1639 95ab4de9 David Knowles
    @param node: node whose tags to return
1640 95ab4de9 David Knowles

1641 95ab4de9 David Knowles
    @rtype: list of str
1642 95ab4de9 David Knowles
    @return: tags for the node
1643 95ab4de9 David Knowles

1644 95ab4de9 David Knowles
    """
1645 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
1646 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1647 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), None, None)
1648 95ab4de9 David Knowles
1649 95ab4de9 David Knowles
  def AddNodeTags(self, node, tags, dry_run=False):
1650 95ab4de9 David Knowles
    """Adds tags to a node.
1651 95ab4de9 David Knowles

1652 95ab4de9 David Knowles
    @type node: str
1653 95ab4de9 David Knowles
    @param node: node to add tags to
1654 95ab4de9 David Knowles
    @type tags: list of str
1655 95ab4de9 David Knowles
    @param tags: tags to add to the node
1656 95ab4de9 David Knowles
    @type dry_run: bool
1657 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1658 95ab4de9 David Knowles

1659 98805538 Michael Hanselmann
    @rtype: string
1660 95ab4de9 David Knowles
    @return: job id
1661 95ab4de9 David Knowles

1662 95ab4de9 David Knowles
    """
1663 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1664 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1665 95ab4de9 David Knowles
1666 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
1667 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1668 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, tags)
1669 95ab4de9 David Knowles
1670 95ab4de9 David Knowles
  def DeleteNodeTags(self, node, tags, dry_run=False):
1671 95ab4de9 David Knowles
    """Delete tags from a node.
1672 95ab4de9 David Knowles

1673 95ab4de9 David Knowles
    @type node: str
1674 95ab4de9 David Knowles
    @param node: node to remove tags from
1675 95ab4de9 David Knowles
    @type tags: list of str
1676 95ab4de9 David Knowles
    @param tags: tags to remove from the node
1677 95ab4de9 David Knowles
    @type dry_run: bool
1678 95ab4de9 David Knowles
    @param dry_run: whether to perform a dry run
1679 95ab4de9 David Knowles

1680 98805538 Michael Hanselmann
    @rtype: string
1681 95ab4de9 David Knowles
    @return: job id
1682 95ab4de9 David Knowles

1683 95ab4de9 David Knowles
    """
1684 95ab4de9 David Knowles
    query = [("tag", t) for t in tags]
1685 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1686 95ab4de9 David Knowles
1687 a198b2d9 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
1688 a198b2d9 Michael Hanselmann
                             ("/%s/nodes/%s/tags" %
1689 a198b2d9 Michael Hanselmann
                              (GANETI_RAPI_VERSION, node)), query, None)
1690 a268af8d Adeodato Simo
1691 4588b4bd Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1692 4588b4bd Dimitris Aragiorgis
    """Gets all networks in the cluster.
1693 4588b4bd Dimitris Aragiorgis

1694 4588b4bd Dimitris Aragiorgis
    @type bulk: bool
1695 4588b4bd Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1696 4588b4bd Dimitris Aragiorgis

1697 4588b4bd Dimitris Aragiorgis
    @rtype: list of dict or str
1698 4588b4bd Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1699 4588b4bd Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1700 4588b4bd Dimitris Aragiorgis

1701 4588b4bd Dimitris Aragiorgis
    """
1702 4588b4bd Dimitris Aragiorgis
    query = []
1703 4588b4bd Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1704 4588b4bd Dimitris Aragiorgis
1705 4588b4bd Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1706 3c286190 Dimitris Aragiorgis
                                 query, None)
1707 4588b4bd Dimitris Aragiorgis
    if bulk:
1708 4588b4bd Dimitris Aragiorgis
      return networks
1709 4588b4bd Dimitris Aragiorgis
    else:
1710 4588b4bd Dimitris Aragiorgis
      return [n["name"] for n in networks]
1711 4588b4bd Dimitris Aragiorgis
1712 4588b4bd Dimitris Aragiorgis
  def GetNetwork(self, network):
1713 4588b4bd Dimitris Aragiorgis
    """Gets information about a network.
1714 4588b4bd Dimitris Aragiorgis

1715 1fa2c40b Dimitris Aragiorgis
    @type network: str
1716 1fa2c40b Dimitris Aragiorgis
    @param network: name of the network whose info to return
1717 4588b4bd Dimitris Aragiorgis

1718 4588b4bd Dimitris Aragiorgis
    @rtype: dict
1719 4588b4bd Dimitris Aragiorgis
    @return: info about the network
1720 4588b4bd Dimitris Aragiorgis

1721 4588b4bd Dimitris Aragiorgis
    """
1722 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1723 4588b4bd Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1724 4588b4bd Dimitris Aragiorgis
                             None, None)
1725 4588b4bd Dimitris Aragiorgis
1726 4588b4bd Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1727 4588b4bd Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None, network_type=None,
1728 8140e24f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=None, dry_run=False):
1729 4588b4bd Dimitris Aragiorgis
    """Creates a new network.
1730 4588b4bd Dimitris Aragiorgis

1731 1fa2c40b Dimitris Aragiorgis
    @type network_name: str
1732 1fa2c40b Dimitris Aragiorgis
    @param network_name: the name of network to create
1733 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1734 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1735 4588b4bd Dimitris Aragiorgis

1736 4588b4bd Dimitris Aragiorgis
    @rtype: string
1737 4588b4bd Dimitris Aragiorgis
    @return: job id
1738 4588b4bd Dimitris Aragiorgis

1739 4588b4bd Dimitris Aragiorgis
    """
1740 4588b4bd Dimitris Aragiorgis
    query = []
1741 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1742 4588b4bd Dimitris Aragiorgis
1743 8140e24f Dimitris Aragiorgis
    if add_reserved_ips:
1744 8140e24f Dimitris Aragiorgis
      add_reserved_ips = add_reserved_ips.split(',')
1745 8140e24f Dimitris Aragiorgis
1746 8140e24f Dimitris Aragiorgis
    if tags:
1747 8140e24f Dimitris Aragiorgis
      tags = tags.split(',')
1748 8140e24f Dimitris Aragiorgis
1749 4588b4bd Dimitris Aragiorgis
    body = {
1750 4588b4bd Dimitris Aragiorgis
      "network_name": network_name,
1751 4588b4bd Dimitris Aragiorgis
      "gateway": gateway,
1752 4588b4bd Dimitris Aragiorgis
      "network": network,
1753 4588b4bd Dimitris Aragiorgis
      "gateway6": gateway6,
1754 4588b4bd Dimitris Aragiorgis
      "network6": network6,
1755 4588b4bd Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1756 4588b4bd Dimitris Aragiorgis
      "network_type": network_type,
1757 8140e24f Dimitris Aragiorgis
      "add_reserved_ips": add_reserved_ips,
1758 8140e24f Dimitris Aragiorgis
      "tags": tags,
1759 4588b4bd Dimitris Aragiorgis
      }
1760 4588b4bd Dimitris Aragiorgis
1761 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1762 4588b4bd Dimitris Aragiorgis
                             query, body)
1763 4588b4bd Dimitris Aragiorgis
1764 6e8091f9 Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link, dry_run=False):
1765 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1766 4588b4bd Dimitris Aragiorgis

1767 4588b4bd Dimitris Aragiorgis
    """
1768 4588b4bd Dimitris Aragiorgis
    body = {
1769 4588b4bd Dimitris Aragiorgis
      "group_name": group_name,
1770 4588b4bd Dimitris Aragiorgis
      "network_mode": mode,
1771 3c286190 Dimitris Aragiorgis
      "network_link": link,
1772 4588b4bd Dimitris Aragiorgis
      }
1773 4588b4bd Dimitris Aragiorgis
1774 6e8091f9 Dimitris Aragiorgis
    query = []
1775 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1776 6e8091f9 Dimitris Aragiorgis
1777 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1778 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1779 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1780 4588b4bd Dimitris Aragiorgis
1781 6e8091f9 Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name, dry_run=False):
1782 4588b4bd Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1783 4588b4bd Dimitris Aragiorgis

1784 4588b4bd Dimitris Aragiorgis
    """
1785 4588b4bd Dimitris Aragiorgis
    body = {
1786 3c286190 Dimitris Aragiorgis
      "group_name": group_name,
1787 4588b4bd Dimitris Aragiorgis
      }
1788 6e8091f9 Dimitris Aragiorgis
1789 6e8091f9 Dimitris Aragiorgis
    query = []
1790 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1791 6e8091f9 Dimitris Aragiorgis
1792 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1793 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1794 6e8091f9 Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1795 6e8091f9 Dimitris Aragiorgis
1796 6e8091f9 Dimitris Aragiorgis
  def ModifyNetwork(self, network, **kwargs):
1797 6e8091f9 Dimitris Aragiorgis
    """Modifies a network.
1798 6e8091f9 Dimitris Aragiorgis

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

1801 6e8091f9 Dimitris Aragiorgis
    @type network: string
1802 6e8091f9 Dimitris Aragiorgis
    @param network: Network name
1803 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1804 6e8091f9 Dimitris Aragiorgis
    @return: job id
1805 4588b4bd Dimitris Aragiorgis

1806 6e8091f9 Dimitris Aragiorgis
    """
1807 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1808 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1809 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1810 4588b4bd Dimitris Aragiorgis
1811 4588b4bd Dimitris Aragiorgis
  def DeleteNetwork(self, network, dry_run=False):
1812 4588b4bd Dimitris Aragiorgis
    """Deletes a network.
1813 4588b4bd Dimitris Aragiorgis

1814 1fa2c40b Dimitris Aragiorgis
    @type network: str
1815 1fa2c40b Dimitris Aragiorgis
    @param network: the network to delete
1816 4588b4bd Dimitris Aragiorgis
    @type dry_run: bool
1817 4588b4bd Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1818 4588b4bd Dimitris Aragiorgis

1819 4588b4bd Dimitris Aragiorgis
    @rtype: string
1820 4588b4bd Dimitris Aragiorgis
    @return: job id
1821 4588b4bd Dimitris Aragiorgis

1822 4588b4bd Dimitris Aragiorgis
    """
1823 4588b4bd Dimitris Aragiorgis
    query = []
1824 4588b4bd Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1825 4588b4bd Dimitris Aragiorgis
1826 4588b4bd Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1827 4588b4bd Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1828 4588b4bd Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1829 4588b4bd Dimitris Aragiorgis
1830 6e8091f9 Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1831 6e8091f9 Dimitris Aragiorgis
    """Gets tags for a network.
1832 6e8091f9 Dimitris Aragiorgis

1833 6e8091f9 Dimitris Aragiorgis
    @type network: string
1834 6e8091f9 Dimitris Aragiorgis
    @param network: Node group whose tags to return
1835 6e8091f9 Dimitris Aragiorgis

1836 6e8091f9 Dimitris Aragiorgis
    @rtype: list of strings
1837 6e8091f9 Dimitris Aragiorgis
    @return: tags for the network
1838 6e8091f9 Dimitris Aragiorgis

1839 6e8091f9 Dimitris Aragiorgis
    """
1840 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1841 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1842 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1843 6e8091f9 Dimitris Aragiorgis
1844 6e8091f9 Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1845 6e8091f9 Dimitris Aragiorgis
    """Adds tags to a network.
1846 6e8091f9 Dimitris Aragiorgis

1847 6e8091f9 Dimitris Aragiorgis
    @type network: str
1848 6e8091f9 Dimitris Aragiorgis
    @param network: network to add tags to
1849 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1850 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to add to the network
1851 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1852 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1853 6e8091f9 Dimitris Aragiorgis

1854 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1855 6e8091f9 Dimitris Aragiorgis
    @return: job id
1856 6e8091f9 Dimitris Aragiorgis

1857 6e8091f9 Dimitris Aragiorgis
    """
1858 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1859 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1860 6e8091f9 Dimitris Aragiorgis
1861 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1862 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1863 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1864 6e8091f9 Dimitris Aragiorgis
1865 6e8091f9 Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1866 6e8091f9 Dimitris Aragiorgis
    """Deletes tags from a network.
1867 6e8091f9 Dimitris Aragiorgis

1868 6e8091f9 Dimitris Aragiorgis
    @type network: str
1869 6e8091f9 Dimitris Aragiorgis
    @param network: network to delete tags from
1870 6e8091f9 Dimitris Aragiorgis
    @type tags: list of string
1871 6e8091f9 Dimitris Aragiorgis
    @param tags: tags to delete
1872 6e8091f9 Dimitris Aragiorgis
    @type dry_run: bool
1873 6e8091f9 Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1874 6e8091f9 Dimitris Aragiorgis
    @rtype: string
1875 6e8091f9 Dimitris Aragiorgis
    @return: job id
1876 6e8091f9 Dimitris Aragiorgis

1877 6e8091f9 Dimitris Aragiorgis
    """
1878 6e8091f9 Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1879 6e8091f9 Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1880 6e8091f9 Dimitris Aragiorgis
1881 6e8091f9 Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1882 6e8091f9 Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1883 6e8091f9 Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1884 6e8091f9 Dimitris Aragiorgis
1885 a268af8d Adeodato Simo
  def GetGroups(self, bulk=False):
1886 a268af8d Adeodato Simo
    """Gets all node groups in the cluster.
1887 a268af8d Adeodato Simo

1888 a268af8d Adeodato Simo
    @type bulk: bool
1889 a268af8d Adeodato Simo
    @param bulk: whether to return all information about the groups
1890 a268af8d Adeodato Simo

1891 a268af8d Adeodato Simo
    @rtype: list of dict or str
1892 a268af8d Adeodato Simo
    @return: if bulk is true, a list of dictionaries with info about all node
1893 a268af8d Adeodato Simo
        groups in the cluster, else a list of names of those node groups
1894 a268af8d Adeodato Simo

1895 a268af8d Adeodato Simo
    """
1896 a268af8d Adeodato Simo
    query = []
1897 4c864b55 Michael Hanselmann
    _AppendIf(query, bulk, ("bulk", 1))
1898 a268af8d Adeodato Simo
1899 a268af8d Adeodato Simo
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1900 a268af8d Adeodato Simo
                               query, None)
1901 a268af8d Adeodato Simo
    if bulk:
1902 a268af8d Adeodato Simo
      return groups
1903 a268af8d Adeodato Simo
    else:
1904 a268af8d Adeodato Simo
      return [g["name"] for g in groups]
1905 a268af8d Adeodato Simo
1906 a268af8d Adeodato Simo
  def GetGroup(self, group):
1907 a268af8d Adeodato Simo
    """Gets information about a node group.
1908 a268af8d Adeodato Simo

1909 a268af8d Adeodato Simo
    @type group: str
1910 a268af8d Adeodato Simo
    @param group: name of the node group whose info to return
1911 a268af8d Adeodato Simo

1912 a268af8d Adeodato Simo
    @rtype: dict
1913 a268af8d Adeodato Simo
    @return: info about the node group
1914 a268af8d Adeodato Simo

1915 a268af8d Adeodato Simo
    """
1916 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_GET,
1917 a268af8d Adeodato Simo
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1918 a268af8d Adeodato Simo
                             None, None)
1919 a268af8d Adeodato Simo
1920 90e99856 Adeodato Simo
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1921 a268af8d Adeodato Simo
    """Creates a new node group.
1922 a268af8d Adeodato Simo

1923 a268af8d Adeodato Simo
    @type name: str
1924 a268af8d Adeodato Simo
    @param name: the name of node group to create
1925 90e99856 Adeodato Simo
    @type alloc_policy: str
1926 90e99856 Adeodato Simo
    @param alloc_policy: the desired allocation policy for the group, if any
1927 a268af8d Adeodato Simo
    @type dry_run: bool
1928 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1929 a268af8d Adeodato Simo

1930 98805538 Michael Hanselmann
    @rtype: string
1931 a268af8d Adeodato Simo
    @return: job id
1932 a268af8d Adeodato Simo

1933 a268af8d Adeodato Simo
    """
1934 a268af8d Adeodato Simo
    query = []
1935 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1936 a268af8d Adeodato Simo
1937 a268af8d Adeodato Simo
    body = {
1938 a268af8d Adeodato Simo
      "name": name,
1939 3c286190 Dimitris Aragiorgis
      "alloc_policy": alloc_policy,
1940 a268af8d Adeodato Simo
      }
1941 a268af8d Adeodato Simo
1942 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1943 a268af8d Adeodato Simo
                             query, body)
1944 a268af8d Adeodato Simo
1945 f18fab7d Adeodato Simo
  def ModifyGroup(self, group, **kwargs):
1946 f18fab7d Adeodato Simo
    """Modifies a node group.
1947 f18fab7d Adeodato Simo

1948 f18fab7d Adeodato Simo
    More details for parameters can be found in the RAPI documentation.
1949 f18fab7d Adeodato Simo

1950 f18fab7d Adeodato Simo
    @type group: string
1951 f18fab7d Adeodato Simo
    @param group: Node group name
1952 98805538 Michael Hanselmann
    @rtype: string
1953 f18fab7d Adeodato Simo
    @return: job id
1954 f18fab7d Adeodato Simo

1955 f18fab7d Adeodato Simo
    """
1956 f18fab7d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1957 f18fab7d Adeodato Simo
                             ("/%s/groups/%s/modify" %
1958 f18fab7d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1959 f18fab7d Adeodato Simo
1960 a268af8d Adeodato Simo
  def DeleteGroup(self, group, dry_run=False):
1961 a268af8d Adeodato Simo
    """Deletes a node group.
1962 a268af8d Adeodato Simo

1963 a268af8d Adeodato Simo
    @type group: str
1964 a268af8d Adeodato Simo
    @param group: the node group to delete
1965 a268af8d Adeodato Simo
    @type dry_run: bool
1966 a268af8d Adeodato Simo
    @param dry_run: whether to peform a dry run
1967 a268af8d Adeodato Simo

1968 98805538 Michael Hanselmann
    @rtype: string
1969 a268af8d Adeodato Simo
    @return: job id
1970 a268af8d Adeodato Simo

1971 a268af8d Adeodato Simo
    """
1972 a268af8d Adeodato Simo
    query = []
1973 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
1974 a268af8d Adeodato Simo
1975 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_DELETE,
1976 a268af8d Adeodato Simo
                             ("/%s/groups/%s" %
1977 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), query, None)
1978 a268af8d Adeodato Simo
1979 a268af8d Adeodato Simo
  def RenameGroup(self, group, new_name):
1980 a268af8d Adeodato Simo
    """Changes the name of a node group.
1981 a268af8d Adeodato Simo

1982 a268af8d Adeodato Simo
    @type group: string
1983 a268af8d Adeodato Simo
    @param group: Node group name
1984 a268af8d Adeodato Simo
    @type new_name: string
1985 a268af8d Adeodato Simo
    @param new_name: New node group name
1986 a268af8d Adeodato Simo

1987 98805538 Michael Hanselmann
    @rtype: string
1988 a268af8d Adeodato Simo
    @return: job id
1989 a268af8d Adeodato Simo

1990 a268af8d Adeodato Simo
    """
1991 a268af8d Adeodato Simo
    body = {
1992 a268af8d Adeodato Simo
      "new_name": new_name,
1993 a268af8d Adeodato Simo
      }
1994 a268af8d Adeodato Simo
1995 a268af8d Adeodato Simo
    return self._SendRequest(HTTP_PUT,
1996 a268af8d Adeodato Simo
                             ("/%s/groups/%s/rename" %
1997 a268af8d Adeodato Simo
                              (GANETI_RAPI_VERSION, group)), None, body)
1998 4245446f Adeodato Simo
1999 4245446f Adeodato Simo
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
2000 4245446f Adeodato Simo
    """Assigns nodes to a group.
2001 4245446f Adeodato Simo

2002 4245446f Adeodato Simo
    @type group: string
2003 6ce90543 Michael Hanselmann
    @param group: Node group name
2004 4245446f Adeodato Simo
    @type nodes: list of strings
2005 4245446f Adeodato Simo
    @param nodes: List of nodes to assign to the group
2006 4245446f Adeodato Simo

2007 98805538 Michael Hanselmann
    @rtype: string
2008 4245446f Adeodato Simo
    @return: job id
2009 4245446f Adeodato Simo

2010 4245446f Adeodato Simo
    """
2011 4245446f Adeodato Simo
    query = []
2012 4c864b55 Michael Hanselmann
    _AppendForceIf(query, force)
2013 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2014 4245446f Adeodato Simo
2015 4245446f Adeodato Simo
    body = {
2016 4245446f Adeodato Simo
      "nodes": nodes,
2017 4245446f Adeodato Simo
      }
2018 4245446f Adeodato Simo
2019 4245446f Adeodato Simo
    return self._SendRequest(HTTP_PUT,
2020 4245446f Adeodato Simo
                             ("/%s/groups/%s/assign-nodes" %
2021 4245446f Adeodato Simo
                             (GANETI_RAPI_VERSION, group)), query, body)
2022 208a6cff Michael Hanselmann
2023 414ebaf1 Michael Hanselmann
  def GetGroupTags(self, group):
2024 414ebaf1 Michael Hanselmann
    """Gets tags for a node group.
2025 414ebaf1 Michael Hanselmann

2026 414ebaf1 Michael Hanselmann
    @type group: string
2027 414ebaf1 Michael Hanselmann
    @param group: Node group whose tags to return
2028 414ebaf1 Michael Hanselmann

2029 414ebaf1 Michael Hanselmann
    @rtype: list of strings
2030 414ebaf1 Michael Hanselmann
    @return: tags for the group
2031 414ebaf1 Michael Hanselmann

2032 414ebaf1 Michael Hanselmann
    """
2033 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2034 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2035 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), None, None)
2036 414ebaf1 Michael Hanselmann
2037 414ebaf1 Michael Hanselmann
  def AddGroupTags(self, group, tags, dry_run=False):
2038 414ebaf1 Michael Hanselmann
    """Adds tags to a node group.
2039 414ebaf1 Michael Hanselmann

2040 414ebaf1 Michael Hanselmann
    @type group: str
2041 414ebaf1 Michael Hanselmann
    @param group: group to add tags to
2042 414ebaf1 Michael Hanselmann
    @type tags: list of string
2043 414ebaf1 Michael Hanselmann
    @param tags: tags to add to the group
2044 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2045 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2046 414ebaf1 Michael Hanselmann

2047 414ebaf1 Michael Hanselmann
    @rtype: string
2048 414ebaf1 Michael Hanselmann
    @return: job id
2049 414ebaf1 Michael Hanselmann

2050 414ebaf1 Michael Hanselmann
    """
2051 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2052 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2053 414ebaf1 Michael Hanselmann
2054 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2055 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2056 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2057 414ebaf1 Michael Hanselmann
2058 414ebaf1 Michael Hanselmann
  def DeleteGroupTags(self, group, tags, dry_run=False):
2059 414ebaf1 Michael Hanselmann
    """Deletes tags from a node group.
2060 414ebaf1 Michael Hanselmann

2061 414ebaf1 Michael Hanselmann
    @type group: str
2062 414ebaf1 Michael Hanselmann
    @param group: group to delete tags from
2063 414ebaf1 Michael Hanselmann
    @type tags: list of string
2064 414ebaf1 Michael Hanselmann
    @param tags: tags to delete
2065 414ebaf1 Michael Hanselmann
    @type dry_run: bool
2066 414ebaf1 Michael Hanselmann
    @param dry_run: whether to perform a dry run
2067 414ebaf1 Michael Hanselmann
    @rtype: string
2068 414ebaf1 Michael Hanselmann
    @return: job id
2069 414ebaf1 Michael Hanselmann

2070 414ebaf1 Michael Hanselmann
    """
2071 414ebaf1 Michael Hanselmann
    query = [("tag", t) for t in tags]
2072 4c864b55 Michael Hanselmann
    _AppendDryRunIf(query, dry_run)
2073 414ebaf1 Michael Hanselmann
2074 414ebaf1 Michael Hanselmann
    return self._SendRequest(HTTP_DELETE,
2075 414ebaf1 Michael Hanselmann
                             ("/%s/groups/%s/tags" %
2076 414ebaf1 Michael Hanselmann
                              (GANETI_RAPI_VERSION, group)), query, None)
2077 414ebaf1 Michael Hanselmann
2078 2e5c33db Iustin Pop
  def Query(self, what, fields, qfilter=None):
2079 208a6cff Michael Hanselmann
    """Retrieves information about resources.
2080 208a6cff Michael Hanselmann

2081 208a6cff Michael Hanselmann
    @type what: string
2082 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2083 208a6cff Michael Hanselmann
    @type fields: list of string
2084 208a6cff Michael Hanselmann
    @param fields: Requested fields
2085 2e5c33db Iustin Pop
    @type qfilter: None or list
2086 2e5c33db Iustin Pop
    @param qfilter: Query filter
2087 208a6cff Michael Hanselmann

2088 208a6cff Michael Hanselmann
    @rtype: string
2089 208a6cff Michael Hanselmann
    @return: job id
2090 208a6cff Michael Hanselmann

2091 208a6cff Michael Hanselmann
    """
2092 208a6cff Michael Hanselmann
    body = {
2093 208a6cff Michael Hanselmann
      "fields": fields,
2094 208a6cff Michael Hanselmann
      }
2095 208a6cff Michael Hanselmann
2096 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
2097 57d8e228 Michael Hanselmann
    # TODO: remove "filter" after 2.7
2098 57d8e228 Michael Hanselmann
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
2099 208a6cff Michael Hanselmann
2100 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_PUT,
2101 208a6cff Michael Hanselmann
                             ("/%s/query/%s" %
2102 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), None, body)
2103 208a6cff Michael Hanselmann
2104 208a6cff Michael Hanselmann
  def QueryFields(self, what, fields=None):
2105 208a6cff Michael Hanselmann
    """Retrieves available fields for a resource.
2106 208a6cff Michael Hanselmann

2107 208a6cff Michael Hanselmann
    @type what: string
2108 208a6cff Michael Hanselmann
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2109 208a6cff Michael Hanselmann
    @type fields: list of string
2110 208a6cff Michael Hanselmann
    @param fields: Requested fields
2111 208a6cff Michael Hanselmann

2112 208a6cff Michael Hanselmann
    @rtype: string
2113 208a6cff Michael Hanselmann
    @return: job id
2114 208a6cff Michael Hanselmann

2115 208a6cff Michael Hanselmann
    """
2116 208a6cff Michael Hanselmann
    query = []
2117 208a6cff Michael Hanselmann
2118 208a6cff Michael Hanselmann
    if fields is not None:
2119 4c864b55 Michael Hanselmann
      _AppendIf(query, True, ("fields", ",".join(fields)))
2120 208a6cff Michael Hanselmann
2121 208a6cff Michael Hanselmann
    return self._SendRequest(HTTP_GET,
2122 208a6cff Michael Hanselmann
                             ("/%s/query/%s/fields" %
2123 208a6cff Michael Hanselmann
                              (GANETI_RAPI_VERSION, what)), query, None)