Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / rapi.py @ 71ec054d

History | View | Annotate | Download (57.8 kB)

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

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

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

132 e5e20779 Faidon Liambotis
  """
133 e5e20779 Faidon Liambotis
  pass
134 e5e20779 Faidon Liambotis
135 e5e20779 Faidon Liambotis
136 a3ad611d Dimitris Aragiorgis
class GanetiApiError(Error):
137 a3ad611d Dimitris Aragiorgis
  """Generic error raised from Ganeti API.
138 a3ad611d Dimitris Aragiorgis

139 a3ad611d Dimitris Aragiorgis
  """
140 a3ad611d Dimitris Aragiorgis
  def __init__(self, msg, code=None):
141 a3ad611d Dimitris Aragiorgis
    Error.__init__(self, msg)
142 a3ad611d Dimitris Aragiorgis
    self.code = code
143 a3ad611d Dimitris Aragiorgis
144 a3ad611d Dimitris Aragiorgis
145 a3ad611d Dimitris Aragiorgis
class CertificateError(GanetiApiError):
146 e5e20779 Faidon Liambotis
  """Raised when a problem is found with the SSL certificate.
147 e5e20779 Faidon Liambotis

148 e5e20779 Faidon Liambotis
  """
149 e5e20779 Faidon Liambotis
  pass
150 e5e20779 Faidon Liambotis
151 e5e20779 Faidon Liambotis
152 a3ad611d Dimitris Aragiorgis
def _AppendIf(container, condition, value):
153 a3ad611d Dimitris Aragiorgis
  """Appends to a list if a condition evaluates to truth.
154 e5e20779 Faidon Liambotis

155 e5e20779 Faidon Liambotis
  """
156 a3ad611d Dimitris Aragiorgis
  if condition:
157 a3ad611d Dimitris Aragiorgis
    container.append(value)
158 a3ad611d Dimitris Aragiorgis
159 a3ad611d Dimitris Aragiorgis
  return condition
160 a3ad611d Dimitris Aragiorgis
161 a3ad611d Dimitris Aragiorgis
162 a3ad611d Dimitris Aragiorgis
def _AppendDryRunIf(container, condition):
163 a3ad611d Dimitris Aragiorgis
  """Appends a "dry-run" parameter if a condition evaluates to truth.
164 a3ad611d Dimitris Aragiorgis

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

172 a3ad611d Dimitris Aragiorgis
  """
173 a3ad611d Dimitris Aragiorgis
  return _AppendIf(container, condition, (_QPARAM_FORCE, 1))
174 a3ad611d Dimitris Aragiorgis
175 a3ad611d Dimitris Aragiorgis
176 a3ad611d Dimitris Aragiorgis
def _SetItemIf(container, condition, item, value):
177 a3ad611d Dimitris Aragiorgis
  """Sets an item if a condition evaluates to truth.
178 a3ad611d Dimitris Aragiorgis

179 a3ad611d Dimitris Aragiorgis
  """
180 a3ad611d Dimitris Aragiorgis
  if condition:
181 a3ad611d Dimitris Aragiorgis
    container[item] = value
182 a3ad611d Dimitris Aragiorgis
183 a3ad611d Dimitris Aragiorgis
  return condition
184 e5e20779 Faidon Liambotis
185 e5e20779 Faidon Liambotis
186 e5e20779 Faidon Liambotis
def UsesRapiClient(fn):
187 e5e20779 Faidon Liambotis
  """Decorator for code using RAPI client to initialize pycURL.
188 e5e20779 Faidon Liambotis

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

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

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

242 e5e20779 Faidon Liambotis
    @type curl: pycurl.Curl
243 e5e20779 Faidon Liambotis
    @param curl: cURL object
244 e5e20779 Faidon Liambotis

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

308 e5e20779 Faidon Liambotis
  """
309 e5e20779 Faidon Liambotis
  USER_AGENT = "Ganeti RAPI Client"
310 a3ad611d Dimitris Aragiorgis
  _json_encoder = simplejson.JSONEncoder(sort_keys=True)
311 e5e20779 Faidon Liambotis
312 e5e20779 Faidon Liambotis
  def __init__(self, host, port=GANETI_RAPI_PORT,
313 a3ad611d Dimitris Aragiorgis
               username=None, password=None, logger=logging,
314 e5e20779 Faidon Liambotis
               curl_config_fn=None, curl_factory=None):
315 e5e20779 Faidon Liambotis
    """Initializes this class.
316 e5e20779 Faidon Liambotis

317 e5e20779 Faidon Liambotis
    @type host: string
318 e5e20779 Faidon Liambotis
    @param host: the ganeti cluster master to interact with
319 e5e20779 Faidon Liambotis
    @type port: int
320 e5e20779 Faidon Liambotis
    @param port: the port on which the RAPI is running (default is 5080)
321 e5e20779 Faidon Liambotis
    @type username: string
322 e5e20779 Faidon Liambotis
    @param username: the username to connect with
323 e5e20779 Faidon Liambotis
    @type password: string
324 e5e20779 Faidon Liambotis
    @param password: the password to connect with
325 e5e20779 Faidon Liambotis
    @type curl_config_fn: callable
326 e5e20779 Faidon Liambotis
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
327 e5e20779 Faidon Liambotis
    @param logger: Logging object
328 e5e20779 Faidon Liambotis

329 e5e20779 Faidon Liambotis
    """
330 e5e20779 Faidon Liambotis
    self._username = username
331 e5e20779 Faidon Liambotis
    self._password = password
332 e5e20779 Faidon Liambotis
    self._logger = logger
333 e5e20779 Faidon Liambotis
    self._curl_config_fn = curl_config_fn
334 e5e20779 Faidon Liambotis
    self._curl_factory = curl_factory
335 e5e20779 Faidon Liambotis
336 e5e20779 Faidon Liambotis
    try:
337 e5e20779 Faidon Liambotis
      socket.inet_pton(socket.AF_INET6, host)
338 e5e20779 Faidon Liambotis
      address = "[%s]:%s" % (host, port)
339 e5e20779 Faidon Liambotis
    except socket.error:
340 e5e20779 Faidon Liambotis
      address = "%s:%s" % (host, port)
341 e5e20779 Faidon Liambotis
342 e5e20779 Faidon Liambotis
    self._base_url = "https://%s" % address
343 e5e20779 Faidon Liambotis
344 e5e20779 Faidon Liambotis
    if username is not None:
345 e5e20779 Faidon Liambotis
      if password is None:
346 e5e20779 Faidon Liambotis
        raise Error("Password not specified")
347 e5e20779 Faidon Liambotis
    elif password:
348 e5e20779 Faidon Liambotis
      raise Error("Specified password without username")
349 e5e20779 Faidon Liambotis
350 e5e20779 Faidon Liambotis
  def _CreateCurl(self):
351 e5e20779 Faidon Liambotis
    """Creates a cURL object.
352 e5e20779 Faidon Liambotis

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

392 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
393 e5e20779 Faidon Liambotis
    @param query: Query arguments
394 e5e20779 Faidon Liambotis
    @rtype: list
395 e5e20779 Faidon Liambotis
    @return: Query list with encoded values
396 e5e20779 Faidon Liambotis

397 e5e20779 Faidon Liambotis
    """
398 e5e20779 Faidon Liambotis
    result = []
399 e5e20779 Faidon Liambotis
400 e5e20779 Faidon Liambotis
    for name, value in query:
401 e5e20779 Faidon Liambotis
      if value is None:
402 e5e20779 Faidon Liambotis
        result.append((name, ""))
403 e5e20779 Faidon Liambotis
404 e5e20779 Faidon Liambotis
      elif isinstance(value, bool):
405 e5e20779 Faidon Liambotis
        # Boolean values must be encoded as 0 or 1
406 e5e20779 Faidon Liambotis
        result.append((name, int(value)))
407 e5e20779 Faidon Liambotis
408 e5e20779 Faidon Liambotis
      elif isinstance(value, (list, tuple, dict)):
409 e5e20779 Faidon Liambotis
        raise ValueError("Invalid query data type %r" % type(value).__name__)
410 e5e20779 Faidon Liambotis
411 e5e20779 Faidon Liambotis
      else:
412 e5e20779 Faidon Liambotis
        result.append((name, value))
413 e5e20779 Faidon Liambotis
414 e5e20779 Faidon Liambotis
    return result
415 e5e20779 Faidon Liambotis
416 e5e20779 Faidon Liambotis
  def _SendRequest(self, method, path, query, content):
417 e5e20779 Faidon Liambotis
    """Sends an HTTP request.
418 e5e20779 Faidon Liambotis

419 e5e20779 Faidon Liambotis
    This constructs a full URL, encodes and decodes HTTP bodies, and
420 e5e20779 Faidon Liambotis
    handles invalid responses in a pythonic way.
421 e5e20779 Faidon Liambotis

422 e5e20779 Faidon Liambotis
    @type method: string
423 e5e20779 Faidon Liambotis
    @param method: HTTP method to use
424 e5e20779 Faidon Liambotis
    @type path: string
425 e5e20779 Faidon Liambotis
    @param path: HTTP URL path
426 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
427 e5e20779 Faidon Liambotis
    @param query: query arguments to pass to urllib.urlencode
428 e5e20779 Faidon Liambotis
    @type content: str or None
429 e5e20779 Faidon Liambotis
    @param content: HTTP body content
430 e5e20779 Faidon Liambotis

431 e5e20779 Faidon Liambotis
    @rtype: str
432 e5e20779 Faidon Liambotis
    @return: JSON-Decoded response
433 e5e20779 Faidon Liambotis

434 e5e20779 Faidon Liambotis
    @raises CertificateError: If an invalid SSL certificate is found
435 e5e20779 Faidon Liambotis
    @raises GanetiApiError: If an invalid response is returned
436 e5e20779 Faidon Liambotis

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

508 e5e20779 Faidon Liambotis
    @rtype: int
509 e5e20779 Faidon Liambotis
    @return: Ganeti Remote API version
510 e5e20779 Faidon Liambotis

511 e5e20779 Faidon Liambotis
    """
512 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/version", None, None)
513 e5e20779 Faidon Liambotis
514 e5e20779 Faidon Liambotis
  def GetFeatures(self):
515 e5e20779 Faidon Liambotis
    """Gets the list of optional features supported by RAPI server.
516 e5e20779 Faidon Liambotis

517 e5e20779 Faidon Liambotis
    @rtype: list
518 e5e20779 Faidon Liambotis
    @return: List of optional features
519 e5e20779 Faidon Liambotis

520 e5e20779 Faidon Liambotis
    """
521 e5e20779 Faidon Liambotis
    try:
522 e5e20779 Faidon Liambotis
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
523 e5e20779 Faidon Liambotis
                               None, None)
524 e5e20779 Faidon Liambotis
    except GanetiApiError, err:
525 e5e20779 Faidon Liambotis
      # Older RAPI servers don't support this resource
526 e5e20779 Faidon Liambotis
      if err.code == HTTP_NOT_FOUND:
527 e5e20779 Faidon Liambotis
        return []
528 e5e20779 Faidon Liambotis
529 e5e20779 Faidon Liambotis
      raise
530 e5e20779 Faidon Liambotis
531 e5e20779 Faidon Liambotis
  def GetOperatingSystems(self):
532 e5e20779 Faidon Liambotis
    """Gets the Operating Systems running in the Ganeti cluster.
533 e5e20779 Faidon Liambotis

534 e5e20779 Faidon Liambotis
    @rtype: list of str
535 e5e20779 Faidon Liambotis
    @return: operating systems
536 e5e20779 Faidon Liambotis

537 e5e20779 Faidon Liambotis
    """
538 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
539 e5e20779 Faidon Liambotis
                             None, None)
540 e5e20779 Faidon Liambotis
541 e5e20779 Faidon Liambotis
  def GetInfo(self):
542 e5e20779 Faidon Liambotis
    """Gets info about the cluster.
543 e5e20779 Faidon Liambotis

544 e5e20779 Faidon Liambotis
    @rtype: dict
545 e5e20779 Faidon Liambotis
    @return: information about the cluster
546 e5e20779 Faidon Liambotis

547 e5e20779 Faidon Liambotis
    """
548 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
549 e5e20779 Faidon Liambotis
                             None, None)
550 e5e20779 Faidon Liambotis
551 067dda99 Vangelis Koukis
  def RedistributeConfig(self):
552 067dda99 Vangelis Koukis
    """Tells the cluster to redistribute its configuration files.
553 067dda99 Vangelis Koukis

554 a3ad611d Dimitris Aragiorgis
    @rtype: string
555 067dda99 Vangelis Koukis
    @return: job id
556 067dda99 Vangelis Koukis

557 067dda99 Vangelis Koukis
    """
558 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
559 067dda99 Vangelis Koukis
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
560 067dda99 Vangelis Koukis
                             None, None)
561 067dda99 Vangelis Koukis
562 067dda99 Vangelis Koukis
  def ModifyCluster(self, **kwargs):
563 067dda99 Vangelis Koukis
    """Modifies cluster parameters.
564 067dda99 Vangelis Koukis

565 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
566 067dda99 Vangelis Koukis

567 a3ad611d Dimitris Aragiorgis
    @rtype: string
568 067dda99 Vangelis Koukis
    @return: job id
569 067dda99 Vangelis Koukis

570 067dda99 Vangelis Koukis
    """
571 067dda99 Vangelis Koukis
    body = kwargs
572 067dda99 Vangelis Koukis
573 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
574 067dda99 Vangelis Koukis
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
575 067dda99 Vangelis Koukis
576 e5e20779 Faidon Liambotis
  def GetClusterTags(self):
577 e5e20779 Faidon Liambotis
    """Gets the cluster tags.
578 e5e20779 Faidon Liambotis

579 e5e20779 Faidon Liambotis
    @rtype: list of str
580 e5e20779 Faidon Liambotis
    @return: cluster tags
581 e5e20779 Faidon Liambotis

582 e5e20779 Faidon Liambotis
    """
583 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
584 e5e20779 Faidon Liambotis
                             None, None)
585 e5e20779 Faidon Liambotis
586 e5e20779 Faidon Liambotis
  def AddClusterTags(self, tags, dry_run=False):
587 e5e20779 Faidon Liambotis
    """Adds tags to the cluster.
588 e5e20779 Faidon Liambotis

589 e5e20779 Faidon Liambotis
    @type tags: list of str
590 e5e20779 Faidon Liambotis
    @param tags: tags to add to the cluster
591 e5e20779 Faidon Liambotis
    @type dry_run: bool
592 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
593 e5e20779 Faidon Liambotis

594 a3ad611d Dimitris Aragiorgis
    @rtype: string
595 e5e20779 Faidon Liambotis
    @return: job id
596 e5e20779 Faidon Liambotis

597 e5e20779 Faidon Liambotis
    """
598 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
599 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
600 e5e20779 Faidon Liambotis
601 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
602 e5e20779 Faidon Liambotis
                             query, None)
603 e5e20779 Faidon Liambotis
604 e5e20779 Faidon Liambotis
  def DeleteClusterTags(self, tags, dry_run=False):
605 e5e20779 Faidon Liambotis
    """Deletes tags from the cluster.
606 e5e20779 Faidon Liambotis

607 e5e20779 Faidon Liambotis
    @type tags: list of str
608 e5e20779 Faidon Liambotis
    @param tags: tags to delete
609 e5e20779 Faidon Liambotis
    @type dry_run: bool
610 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
611 a3ad611d Dimitris Aragiorgis
    @rtype: string
612 a3ad611d Dimitris Aragiorgis
    @return: job id
613 e5e20779 Faidon Liambotis

614 e5e20779 Faidon Liambotis
    """
615 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
616 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
617 e5e20779 Faidon Liambotis
618 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
619 e5e20779 Faidon Liambotis
                             query, None)
620 e5e20779 Faidon Liambotis
621 e5e20779 Faidon Liambotis
  def GetInstances(self, bulk=False):
622 e5e20779 Faidon Liambotis
    """Gets information about instances on the cluster.
623 e5e20779 Faidon Liambotis

624 e5e20779 Faidon Liambotis
    @type bulk: bool
625 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
626 e5e20779 Faidon Liambotis

627 e5e20779 Faidon Liambotis
    @rtype: list of dict or list of str
628 e5e20779 Faidon Liambotis
    @return: if bulk is True, info about the instances, else a list of instances
629 e5e20779 Faidon Liambotis

630 e5e20779 Faidon Liambotis
    """
631 e5e20779 Faidon Liambotis
    query = []
632 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
633 e5e20779 Faidon Liambotis
634 e5e20779 Faidon Liambotis
    instances = self._SendRequest(HTTP_GET,
635 e5e20779 Faidon Liambotis
                                  "/%s/instances" % GANETI_RAPI_VERSION,
636 e5e20779 Faidon Liambotis
                                  query, None)
637 e5e20779 Faidon Liambotis
    if bulk:
638 e5e20779 Faidon Liambotis
      return instances
639 e5e20779 Faidon Liambotis
    else:
640 e5e20779 Faidon Liambotis
      return [i["id"] for i in instances]
641 e5e20779 Faidon Liambotis
642 e5e20779 Faidon Liambotis
  def GetInstance(self, instance):
643 e5e20779 Faidon Liambotis
    """Gets information about an instance.
644 e5e20779 Faidon Liambotis

645 e5e20779 Faidon Liambotis
    @type instance: str
646 e5e20779 Faidon Liambotis
    @param instance: instance whose info to return
647 e5e20779 Faidon Liambotis

648 e5e20779 Faidon Liambotis
    @rtype: dict
649 e5e20779 Faidon Liambotis
    @return: info about the instance
650 e5e20779 Faidon Liambotis

651 e5e20779 Faidon Liambotis
    """
652 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
653 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
654 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
655 e5e20779 Faidon Liambotis
656 e5e20779 Faidon Liambotis
  def GetInstanceInfo(self, instance, static=None):
657 e5e20779 Faidon Liambotis
    """Gets information about an instance.
658 e5e20779 Faidon Liambotis

659 e5e20779 Faidon Liambotis
    @type instance: string
660 e5e20779 Faidon Liambotis
    @param instance: Instance name
661 e5e20779 Faidon Liambotis
    @rtype: string
662 e5e20779 Faidon Liambotis
    @return: Job ID
663 e5e20779 Faidon Liambotis

664 e5e20779 Faidon Liambotis
    """
665 e5e20779 Faidon Liambotis
    if static is not None:
666 e5e20779 Faidon Liambotis
      query = [("static", static)]
667 e5e20779 Faidon Liambotis
    else:
668 e5e20779 Faidon Liambotis
      query = None
669 e5e20779 Faidon Liambotis
670 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
671 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/info" %
672 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
673 e5e20779 Faidon Liambotis
674 e5e20779 Faidon Liambotis
  def CreateInstance(self, mode, name, disk_template, disks, nics,
675 e5e20779 Faidon Liambotis
                     **kwargs):
676 e5e20779 Faidon Liambotis
    """Creates a new instance.
677 e5e20779 Faidon Liambotis

678 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
679 e5e20779 Faidon Liambotis

680 e5e20779 Faidon Liambotis
    @type mode: string
681 e5e20779 Faidon Liambotis
    @param mode: Instance creation mode
682 e5e20779 Faidon Liambotis
    @type name: string
683 e5e20779 Faidon Liambotis
    @param name: Hostname of the instance to create
684 e5e20779 Faidon Liambotis
    @type disk_template: string
685 e5e20779 Faidon Liambotis
    @param disk_template: Disk template for instance (e.g. plain, diskless,
686 e5e20779 Faidon Liambotis
                          file, or drbd)
687 e5e20779 Faidon Liambotis
    @type disks: list of dicts
688 e5e20779 Faidon Liambotis
    @param disks: List of disk definitions
689 e5e20779 Faidon Liambotis
    @type nics: list of dicts
690 e5e20779 Faidon Liambotis
    @param nics: List of NIC definitions
691 e5e20779 Faidon Liambotis
    @type dry_run: bool
692 e5e20779 Faidon Liambotis
    @keyword dry_run: whether to perform a dry run
693 e5e20779 Faidon Liambotis

694 a3ad611d Dimitris Aragiorgis
    @rtype: string
695 e5e20779 Faidon Liambotis
    @return: job id
696 e5e20779 Faidon Liambotis

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

730 e5e20779 Faidon Liambotis
    @type instance: str
731 e5e20779 Faidon Liambotis
    @param instance: the instance to delete
732 e5e20779 Faidon Liambotis

733 a3ad611d Dimitris Aragiorgis
    @rtype: string
734 e5e20779 Faidon Liambotis
    @return: job id
735 e5e20779 Faidon Liambotis

736 e5e20779 Faidon Liambotis
    """
737 e5e20779 Faidon Liambotis
    query = []
738 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
739 e5e20779 Faidon Liambotis
740 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
741 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
742 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
743 e5e20779 Faidon Liambotis
744 e5e20779 Faidon Liambotis
  def ModifyInstance(self, instance, **kwargs):
745 e5e20779 Faidon Liambotis
    """Modifies an instance.
746 e5e20779 Faidon Liambotis

747 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
748 e5e20779 Faidon Liambotis

749 e5e20779 Faidon Liambotis
    @type instance: string
750 e5e20779 Faidon Liambotis
    @param instance: Instance name
751 a3ad611d Dimitris Aragiorgis
    @rtype: string
752 e5e20779 Faidon Liambotis
    @return: job id
753 e5e20779 Faidon Liambotis

754 e5e20779 Faidon Liambotis
    """
755 e5e20779 Faidon Liambotis
    body = kwargs
756 e5e20779 Faidon Liambotis
757 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
758 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/modify" %
759 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
760 e5e20779 Faidon Liambotis
761 067dda99 Vangelis Koukis
  def ActivateInstanceDisks(self, instance, ignore_size=None):
762 067dda99 Vangelis Koukis
    """Activates an instance's disks.
763 067dda99 Vangelis Koukis

764 067dda99 Vangelis Koukis
    @type instance: string
765 067dda99 Vangelis Koukis
    @param instance: Instance name
766 067dda99 Vangelis Koukis
    @type ignore_size: bool
767 067dda99 Vangelis Koukis
    @param ignore_size: Whether to ignore recorded size
768 a3ad611d Dimitris Aragiorgis
    @rtype: string
769 067dda99 Vangelis Koukis
    @return: job id
770 067dda99 Vangelis Koukis

771 067dda99 Vangelis Koukis
    """
772 067dda99 Vangelis Koukis
    query = []
773 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, ignore_size, ("ignore_size", 1))
774 067dda99 Vangelis Koukis
775 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
776 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/activate-disks" %
777 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), query, None)
778 067dda99 Vangelis Koukis
779 067dda99 Vangelis Koukis
  def DeactivateInstanceDisks(self, instance):
780 067dda99 Vangelis Koukis
    """Deactivates an instance's disks.
781 067dda99 Vangelis Koukis

782 067dda99 Vangelis Koukis
    @type instance: string
783 067dda99 Vangelis Koukis
    @param instance: Instance name
784 a3ad611d Dimitris Aragiorgis
    @rtype: string
785 067dda99 Vangelis Koukis
    @return: job id
786 067dda99 Vangelis Koukis

787 067dda99 Vangelis Koukis
    """
788 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
789 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/deactivate-disks" %
790 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
791 067dda99 Vangelis Koukis
792 a3ad611d Dimitris Aragiorgis
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
793 a3ad611d Dimitris Aragiorgis
    """Recreate an instance's disks.
794 a3ad611d Dimitris Aragiorgis

795 a3ad611d Dimitris Aragiorgis
    @type instance: string
796 a3ad611d Dimitris Aragiorgis
    @param instance: Instance name
797 a3ad611d Dimitris Aragiorgis
    @type disks: list of int
798 a3ad611d Dimitris Aragiorgis
    @param disks: List of disk indexes
799 a3ad611d Dimitris Aragiorgis
    @type nodes: list of string
800 a3ad611d Dimitris Aragiorgis
    @param nodes: New instance nodes, if relocation is desired
801 a3ad611d Dimitris Aragiorgis
    @rtype: string
802 a3ad611d Dimitris Aragiorgis
    @return: job id
803 a3ad611d Dimitris Aragiorgis

804 a3ad611d Dimitris Aragiorgis
    """
805 a3ad611d Dimitris Aragiorgis
    body = {}
806 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, disks is not None, "disks", disks)
807 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, nodes is not None, "nodes", nodes)
808 a3ad611d Dimitris Aragiorgis
809 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
810 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/recreate-disks" %
811 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), None, body)
812 a3ad611d Dimitris Aragiorgis
813 067dda99 Vangelis Koukis
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
814 067dda99 Vangelis Koukis
    """Grows a disk of an instance.
815 067dda99 Vangelis Koukis

816 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
817 067dda99 Vangelis Koukis

818 067dda99 Vangelis Koukis
    @type instance: string
819 067dda99 Vangelis Koukis
    @param instance: Instance name
820 067dda99 Vangelis Koukis
    @type disk: integer
821 067dda99 Vangelis Koukis
    @param disk: Disk index
822 067dda99 Vangelis Koukis
    @type amount: integer
823 067dda99 Vangelis Koukis
    @param amount: Grow disk by this amount (MiB)
824 067dda99 Vangelis Koukis
    @type wait_for_sync: bool
825 067dda99 Vangelis Koukis
    @param wait_for_sync: Wait for disk to synchronize
826 a3ad611d Dimitris Aragiorgis
    @rtype: string
827 067dda99 Vangelis Koukis
    @return: job id
828 067dda99 Vangelis Koukis

829 067dda99 Vangelis Koukis
    """
830 067dda99 Vangelis Koukis
    body = {
831 067dda99 Vangelis Koukis
      "amount": amount,
832 067dda99 Vangelis Koukis
      }
833 067dda99 Vangelis Koukis
834 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
835 067dda99 Vangelis Koukis
836 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST,
837 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/disk/%s/grow" %
838 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance, disk)),
839 067dda99 Vangelis Koukis
                             None, body)
840 067dda99 Vangelis Koukis
841 e5e20779 Faidon Liambotis
  def GetInstanceTags(self, instance):
842 e5e20779 Faidon Liambotis
    """Gets tags for an instance.
843 e5e20779 Faidon Liambotis

844 e5e20779 Faidon Liambotis
    @type instance: str
845 e5e20779 Faidon Liambotis
    @param instance: instance whose tags to return
846 e5e20779 Faidon Liambotis

847 e5e20779 Faidon Liambotis
    @rtype: list of str
848 e5e20779 Faidon Liambotis
    @return: tags for the instance
849 e5e20779 Faidon Liambotis

850 e5e20779 Faidon Liambotis
    """
851 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
852 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
853 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
854 e5e20779 Faidon Liambotis
855 e5e20779 Faidon Liambotis
  def AddInstanceTags(self, instance, tags, dry_run=False):
856 e5e20779 Faidon Liambotis
    """Adds tags to an instance.
857 e5e20779 Faidon Liambotis

858 e5e20779 Faidon Liambotis
    @type instance: str
859 e5e20779 Faidon Liambotis
    @param instance: instance to add tags to
860 e5e20779 Faidon Liambotis
    @type tags: list of str
861 e5e20779 Faidon Liambotis
    @param tags: tags to add to the instance
862 e5e20779 Faidon Liambotis
    @type dry_run: bool
863 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
864 e5e20779 Faidon Liambotis

865 a3ad611d Dimitris Aragiorgis
    @rtype: string
866 e5e20779 Faidon Liambotis
    @return: job id
867 e5e20779 Faidon Liambotis

868 e5e20779 Faidon Liambotis
    """
869 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
870 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
871 e5e20779 Faidon Liambotis
872 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
873 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
874 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
875 e5e20779 Faidon Liambotis
876 e5e20779 Faidon Liambotis
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
877 e5e20779 Faidon Liambotis
    """Deletes tags from an instance.
878 e5e20779 Faidon Liambotis

879 e5e20779 Faidon Liambotis
    @type instance: str
880 e5e20779 Faidon Liambotis
    @param instance: instance to delete tags from
881 e5e20779 Faidon Liambotis
    @type tags: list of str
882 e5e20779 Faidon Liambotis
    @param tags: tags to delete
883 e5e20779 Faidon Liambotis
    @type dry_run: bool
884 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
885 a3ad611d Dimitris Aragiorgis
    @rtype: string
886 a3ad611d Dimitris Aragiorgis
    @return: job id
887 e5e20779 Faidon Liambotis

888 e5e20779 Faidon Liambotis
    """
889 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
890 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
891 e5e20779 Faidon Liambotis
892 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
893 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
894 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
895 e5e20779 Faidon Liambotis
896 e5e20779 Faidon Liambotis
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
897 e5e20779 Faidon Liambotis
                     dry_run=False):
898 e5e20779 Faidon Liambotis
    """Reboots an instance.
899 e5e20779 Faidon Liambotis

900 e5e20779 Faidon Liambotis
    @type instance: str
901 e5e20779 Faidon Liambotis
    @param instance: instance to rebot
902 e5e20779 Faidon Liambotis
    @type reboot_type: str
903 e5e20779 Faidon Liambotis
    @param reboot_type: one of: hard, soft, full
904 e5e20779 Faidon Liambotis
    @type ignore_secondaries: bool
905 e5e20779 Faidon Liambotis
    @param ignore_secondaries: if True, ignores errors for the secondary node
906 e5e20779 Faidon Liambotis
        while re-assembling disks (in hard-reboot mode only)
907 e5e20779 Faidon Liambotis
    @type dry_run: bool
908 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
909 a3ad611d Dimitris Aragiorgis
    @rtype: string
910 a3ad611d Dimitris Aragiorgis
    @return: job id
911 e5e20779 Faidon Liambotis

912 e5e20779 Faidon Liambotis
    """
913 e5e20779 Faidon Liambotis
    query = []
914 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
915 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, reboot_type, ("type", reboot_type))
916 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, ignore_secondaries is not None,
917 a3ad611d Dimitris Aragiorgis
              ("ignore_secondaries", ignore_secondaries))
918 e5e20779 Faidon Liambotis
919 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
920 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/reboot" %
921 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
922 e5e20779 Faidon Liambotis
923 a3ad611d Dimitris Aragiorgis
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False):
924 e5e20779 Faidon Liambotis
    """Shuts down an instance.
925 e5e20779 Faidon Liambotis

926 e5e20779 Faidon Liambotis
    @type instance: str
927 e5e20779 Faidon Liambotis
    @param instance: the instance to shut down
928 e5e20779 Faidon Liambotis
    @type dry_run: bool
929 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
930 a3ad611d Dimitris Aragiorgis
    @type no_remember: bool
931 a3ad611d Dimitris Aragiorgis
    @param no_remember: if true, will not record the state change
932 a3ad611d Dimitris Aragiorgis
    @rtype: string
933 a3ad611d Dimitris Aragiorgis
    @return: job id
934 e5e20779 Faidon Liambotis

935 e5e20779 Faidon Liambotis
    """
936 e5e20779 Faidon Liambotis
    query = []
937 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
938 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, no_remember, ("no-remember", 1))
939 e5e20779 Faidon Liambotis
940 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
941 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/shutdown" %
942 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
943 e5e20779 Faidon Liambotis
944 a3ad611d Dimitris Aragiorgis
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
945 e5e20779 Faidon Liambotis
    """Starts up an instance.
946 e5e20779 Faidon Liambotis

947 e5e20779 Faidon Liambotis
    @type instance: str
948 e5e20779 Faidon Liambotis
    @param instance: the instance to start up
949 e5e20779 Faidon Liambotis
    @type dry_run: bool
950 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
951 a3ad611d Dimitris Aragiorgis
    @type no_remember: bool
952 a3ad611d Dimitris Aragiorgis
    @param no_remember: if true, will not record the state change
953 a3ad611d Dimitris Aragiorgis
    @rtype: string
954 a3ad611d Dimitris Aragiorgis
    @return: job id
955 e5e20779 Faidon Liambotis

956 e5e20779 Faidon Liambotis
    """
957 e5e20779 Faidon Liambotis
    query = []
958 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
959 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, no_remember, ("no-remember", 1))
960 e5e20779 Faidon Liambotis
961 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
962 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/startup" %
963 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
964 e5e20779 Faidon Liambotis
965 067dda99 Vangelis Koukis
  def ReinstallInstance(self, instance, os=None, no_startup=False,
966 067dda99 Vangelis Koukis
                        osparams=None):
967 e5e20779 Faidon Liambotis
    """Reinstalls an instance.
968 e5e20779 Faidon Liambotis

969 e5e20779 Faidon Liambotis
    @type instance: str
970 e5e20779 Faidon Liambotis
    @param instance: The instance to reinstall
971 e5e20779 Faidon Liambotis
    @type os: str or None
972 e5e20779 Faidon Liambotis
    @param os: The operating system to reinstall. If None, the instance's
973 e5e20779 Faidon Liambotis
        current operating system will be installed again
974 e5e20779 Faidon Liambotis
    @type no_startup: bool
975 e5e20779 Faidon Liambotis
    @param no_startup: Whether to start the instance automatically
976 a3ad611d Dimitris Aragiorgis
    @rtype: string
977 a3ad611d Dimitris Aragiorgis
    @return: job id
978 e5e20779 Faidon Liambotis

979 e5e20779 Faidon Liambotis
    """
980 067dda99 Vangelis Koukis
    if _INST_REINSTALL_REQV1 in self.GetFeatures():
981 067dda99 Vangelis Koukis
      body = {
982 067dda99 Vangelis Koukis
        "start": not no_startup,
983 067dda99 Vangelis Koukis
        }
984 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, os is not None, "os", os)
985 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, osparams is not None, "osparams", osparams)
986 067dda99 Vangelis Koukis
      return self._SendRequest(HTTP_POST,
987 067dda99 Vangelis Koukis
                               ("/%s/instances/%s/reinstall" %
988 067dda99 Vangelis Koukis
                                (GANETI_RAPI_VERSION, instance)), None, body)
989 067dda99 Vangelis Koukis
990 067dda99 Vangelis Koukis
    # Use old request format
991 067dda99 Vangelis Koukis
    if osparams:
992 067dda99 Vangelis Koukis
      raise GanetiApiError("Server does not support specifying OS parameters"
993 067dda99 Vangelis Koukis
                           " for instance reinstallation")
994 067dda99 Vangelis Koukis
995 e5e20779 Faidon Liambotis
    query = []
996 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, os, ("os", os))
997 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, no_startup, ("nostartup", 1))
998 a3ad611d Dimitris Aragiorgis
999 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1000 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/reinstall" %
1001 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1002 e5e20779 Faidon Liambotis
1003 e5e20779 Faidon Liambotis
  def ReplaceInstanceDisks(self, instance, disks=None, mode=REPLACE_DISK_AUTO,
1004 a3ad611d Dimitris Aragiorgis
                           remote_node=None, iallocator=None):
1005 e5e20779 Faidon Liambotis
    """Replaces disks on an instance.
1006 e5e20779 Faidon Liambotis

1007 e5e20779 Faidon Liambotis
    @type instance: str
1008 e5e20779 Faidon Liambotis
    @param instance: instance whose disks to replace
1009 e5e20779 Faidon Liambotis
    @type disks: list of ints
1010 e5e20779 Faidon Liambotis
    @param disks: Indexes of disks to replace
1011 e5e20779 Faidon Liambotis
    @type mode: str
1012 e5e20779 Faidon Liambotis
    @param mode: replacement mode to use (defaults to replace_auto)
1013 e5e20779 Faidon Liambotis
    @type remote_node: str or None
1014 e5e20779 Faidon Liambotis
    @param remote_node: new secondary node to use (for use with
1015 e5e20779 Faidon Liambotis
        replace_new_secondary mode)
1016 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1017 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator plugin to use (for use with
1018 e5e20779 Faidon Liambotis
                       replace_auto mode)
1019 e5e20779 Faidon Liambotis

1020 a3ad611d Dimitris Aragiorgis
    @rtype: string
1021 e5e20779 Faidon Liambotis
    @return: job id
1022 e5e20779 Faidon Liambotis

1023 e5e20779 Faidon Liambotis
    """
1024 e5e20779 Faidon Liambotis
    query = [
1025 e5e20779 Faidon Liambotis
      ("mode", mode),
1026 e5e20779 Faidon Liambotis
      ]
1027 e5e20779 Faidon Liambotis
1028 a3ad611d Dimitris Aragiorgis
    # TODO: Convert to body parameters
1029 e5e20779 Faidon Liambotis
1030 a3ad611d Dimitris Aragiorgis
    if disks is not None:
1031 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, True,
1032 a3ad611d Dimitris Aragiorgis
                ("disks", ",".join(str(idx) for idx in disks)))
1033 e5e20779 Faidon Liambotis
1034 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1035 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1036 e5e20779 Faidon Liambotis
1037 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1038 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/replace-disks" %
1039 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1040 e5e20779 Faidon Liambotis
1041 e5e20779 Faidon Liambotis
  def PrepareExport(self, instance, mode):
1042 e5e20779 Faidon Liambotis
    """Prepares an instance for an export.
1043 e5e20779 Faidon Liambotis

1044 e5e20779 Faidon Liambotis
    @type instance: string
1045 e5e20779 Faidon Liambotis
    @param instance: Instance name
1046 e5e20779 Faidon Liambotis
    @type mode: string
1047 e5e20779 Faidon Liambotis
    @param mode: Export mode
1048 e5e20779 Faidon Liambotis
    @rtype: string
1049 e5e20779 Faidon Liambotis
    @return: Job ID
1050 e5e20779 Faidon Liambotis

1051 e5e20779 Faidon Liambotis
    """
1052 e5e20779 Faidon Liambotis
    query = [("mode", mode)]
1053 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1054 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/prepare-export" %
1055 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1056 e5e20779 Faidon Liambotis
1057 e5e20779 Faidon Liambotis
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1058 e5e20779 Faidon Liambotis
                     remove_instance=None,
1059 e5e20779 Faidon Liambotis
                     x509_key_name=None, destination_x509_ca=None):
1060 e5e20779 Faidon Liambotis
    """Exports an instance.
1061 e5e20779 Faidon Liambotis

1062 e5e20779 Faidon Liambotis
    @type instance: string
1063 e5e20779 Faidon Liambotis
    @param instance: Instance name
1064 e5e20779 Faidon Liambotis
    @type mode: string
1065 e5e20779 Faidon Liambotis
    @param mode: Export mode
1066 e5e20779 Faidon Liambotis
    @rtype: string
1067 e5e20779 Faidon Liambotis
    @return: Job ID
1068 e5e20779 Faidon Liambotis

1069 e5e20779 Faidon Liambotis
    """
1070 e5e20779 Faidon Liambotis
    body = {
1071 e5e20779 Faidon Liambotis
      "destination": destination,
1072 e5e20779 Faidon Liambotis
      "mode": mode,
1073 e5e20779 Faidon Liambotis
      }
1074 e5e20779 Faidon Liambotis
1075 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1076 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, remove_instance is not None,
1077 a3ad611d Dimitris Aragiorgis
               "remove_instance", remove_instance)
1078 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1079 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, destination_x509_ca is not None,
1080 a3ad611d Dimitris Aragiorgis
               "destination_x509_ca", destination_x509_ca)
1081 e5e20779 Faidon Liambotis
1082 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1083 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/export" %
1084 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1085 e5e20779 Faidon Liambotis
1086 e5e20779 Faidon Liambotis
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1087 e5e20779 Faidon Liambotis
    """Migrates an instance.
1088 e5e20779 Faidon Liambotis

1089 e5e20779 Faidon Liambotis
    @type instance: string
1090 e5e20779 Faidon Liambotis
    @param instance: Instance name
1091 e5e20779 Faidon Liambotis
    @type mode: string
1092 e5e20779 Faidon Liambotis
    @param mode: Migration mode
1093 e5e20779 Faidon Liambotis
    @type cleanup: bool
1094 e5e20779 Faidon Liambotis
    @param cleanup: Whether to clean up a previously failed migration
1095 a3ad611d Dimitris Aragiorgis
    @rtype: string
1096 a3ad611d Dimitris Aragiorgis
    @return: job id
1097 e5e20779 Faidon Liambotis

1098 e5e20779 Faidon Liambotis
    """
1099 e5e20779 Faidon Liambotis
    body = {}
1100 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, mode is not None, "mode", mode)
1101 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1102 e5e20779 Faidon Liambotis
1103 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1104 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/migrate" %
1105 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1106 e5e20779 Faidon Liambotis
1107 a3ad611d Dimitris Aragiorgis
  def FailoverInstance(self, instance, iallocator=None,
1108 a3ad611d Dimitris Aragiorgis
                       ignore_consistency=None, target_node=None):
1109 a3ad611d Dimitris Aragiorgis
    """Does a failover of an instance.
1110 a3ad611d Dimitris Aragiorgis

1111 a3ad611d Dimitris Aragiorgis
    @type instance: string
1112 a3ad611d Dimitris Aragiorgis
    @param instance: Instance name
1113 a3ad611d Dimitris Aragiorgis
    @type iallocator: string
1114 a3ad611d Dimitris Aragiorgis
    @param iallocator: Iallocator for deciding the target node for
1115 a3ad611d Dimitris Aragiorgis
      shared-storage instances
1116 a3ad611d Dimitris Aragiorgis
    @type ignore_consistency: bool
1117 a3ad611d Dimitris Aragiorgis
    @param ignore_consistency: Whether to ignore disk consistency
1118 a3ad611d Dimitris Aragiorgis
    @type target_node: string
1119 a3ad611d Dimitris Aragiorgis
    @param target_node: Target node for shared-storage instances
1120 a3ad611d Dimitris Aragiorgis
    @rtype: string
1121 a3ad611d Dimitris Aragiorgis
    @return: job id
1122 a3ad611d Dimitris Aragiorgis

1123 a3ad611d Dimitris Aragiorgis
    """
1124 a3ad611d Dimitris Aragiorgis
    body = {}
1125 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1126 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, ignore_consistency is not None,
1127 a3ad611d Dimitris Aragiorgis
               "ignore_consistency", ignore_consistency)
1128 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1129 e5e20779 Faidon Liambotis
1130 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1131 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/failover" %
1132 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1133 e5e20779 Faidon Liambotis
1134 e5e20779 Faidon Liambotis
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1135 e5e20779 Faidon Liambotis
    """Changes the name of an instance.
1136 e5e20779 Faidon Liambotis

1137 e5e20779 Faidon Liambotis
    @type instance: string
1138 e5e20779 Faidon Liambotis
    @param instance: Instance name
1139 e5e20779 Faidon Liambotis
    @type new_name: string
1140 e5e20779 Faidon Liambotis
    @param new_name: New instance name
1141 e5e20779 Faidon Liambotis
    @type ip_check: bool
1142 e5e20779 Faidon Liambotis
    @param ip_check: Whether to ensure instance's IP address is inactive
1143 e5e20779 Faidon Liambotis
    @type name_check: bool
1144 e5e20779 Faidon Liambotis
    @param name_check: Whether to ensure instance's name is resolvable
1145 a3ad611d Dimitris Aragiorgis
    @rtype: string
1146 a3ad611d Dimitris Aragiorgis
    @return: job id
1147 e5e20779 Faidon Liambotis

1148 e5e20779 Faidon Liambotis
    """
1149 e5e20779 Faidon Liambotis
    body = {
1150 e5e20779 Faidon Liambotis
      "new_name": new_name,
1151 e5e20779 Faidon Liambotis
      }
1152 e5e20779 Faidon Liambotis
1153 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1154 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1155 e5e20779 Faidon Liambotis
1156 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1157 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/rename" %
1158 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1159 e5e20779 Faidon Liambotis
1160 067dda99 Vangelis Koukis
  def GetInstanceConsole(self, instance):
1161 067dda99 Vangelis Koukis
    """Request information for connecting to instance's console.
1162 067dda99 Vangelis Koukis

1163 067dda99 Vangelis Koukis
    @type instance: string
1164 067dda99 Vangelis Koukis
    @param instance: Instance name
1165 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1166 a3ad611d Dimitris Aragiorgis
    @return: dictionary containing information about instance's console
1167 067dda99 Vangelis Koukis

1168 067dda99 Vangelis Koukis
    """
1169 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1170 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/console" %
1171 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
1172 067dda99 Vangelis Koukis
1173 e5e20779 Faidon Liambotis
  def GetJobs(self):
1174 e5e20779 Faidon Liambotis
    """Gets all jobs for the cluster.
1175 e5e20779 Faidon Liambotis

1176 e5e20779 Faidon Liambotis
    @rtype: list of int
1177 e5e20779 Faidon Liambotis
    @return: job ids for the cluster
1178 e5e20779 Faidon Liambotis

1179 e5e20779 Faidon Liambotis
    """
1180 e5e20779 Faidon Liambotis
    return [int(j["id"])
1181 e5e20779 Faidon Liambotis
            for j in self._SendRequest(HTTP_GET,
1182 e5e20779 Faidon Liambotis
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1183 e5e20779 Faidon Liambotis
                                       None, None)]
1184 e5e20779 Faidon Liambotis
1185 e5e20779 Faidon Liambotis
  def GetJobStatus(self, job_id):
1186 e5e20779 Faidon Liambotis
    """Gets the status of a job.
1187 e5e20779 Faidon Liambotis

1188 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1189 e5e20779 Faidon Liambotis
    @param job_id: job id whose status to query
1190 e5e20779 Faidon Liambotis

1191 e5e20779 Faidon Liambotis
    @rtype: dict
1192 e5e20779 Faidon Liambotis
    @return: job status
1193 e5e20779 Faidon Liambotis

1194 e5e20779 Faidon Liambotis
    """
1195 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1196 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1197 e5e20779 Faidon Liambotis
                             None, None)
1198 e5e20779 Faidon Liambotis
1199 a3ad611d Dimitris Aragiorgis
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1200 a3ad611d Dimitris Aragiorgis
    """Polls cluster for job status until completion.
1201 a3ad611d Dimitris Aragiorgis

1202 a3ad611d Dimitris Aragiorgis
    Completion is defined as any of the following states listed in
1203 a3ad611d Dimitris Aragiorgis
    L{JOB_STATUS_FINALIZED}.
1204 a3ad611d Dimitris Aragiorgis

1205 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1206 a3ad611d Dimitris Aragiorgis
    @param job_id: job id to watch
1207 a3ad611d Dimitris Aragiorgis
    @type period: int
1208 a3ad611d Dimitris Aragiorgis
    @param period: how often to poll for status (optional, default 5s)
1209 a3ad611d Dimitris Aragiorgis
    @type retries: int
1210 a3ad611d Dimitris Aragiorgis
    @param retries: how many time to poll before giving up
1211 a3ad611d Dimitris Aragiorgis
                    (optional, default -1 means unlimited)
1212 a3ad611d Dimitris Aragiorgis

1213 a3ad611d Dimitris Aragiorgis
    @rtype: bool
1214 a3ad611d Dimitris Aragiorgis
    @return: C{True} if job succeeded or C{False} if failed/status timeout
1215 a3ad611d Dimitris Aragiorgis
    @deprecated: It is recommended to use L{WaitForJobChange} wherever
1216 a3ad611d Dimitris Aragiorgis
      possible; L{WaitForJobChange} returns immediately after a job changed and
1217 a3ad611d Dimitris Aragiorgis
      does not use polling
1218 a3ad611d Dimitris Aragiorgis

1219 a3ad611d Dimitris Aragiorgis
    """
1220 a3ad611d Dimitris Aragiorgis
    while retries != 0:
1221 a3ad611d Dimitris Aragiorgis
      job_result = self.GetJobStatus(job_id)
1222 a3ad611d Dimitris Aragiorgis
1223 a3ad611d Dimitris Aragiorgis
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1224 a3ad611d Dimitris Aragiorgis
        return True
1225 a3ad611d Dimitris Aragiorgis
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1226 a3ad611d Dimitris Aragiorgis
        return False
1227 a3ad611d Dimitris Aragiorgis
1228 a3ad611d Dimitris Aragiorgis
      if period:
1229 a3ad611d Dimitris Aragiorgis
        time.sleep(period)
1230 a3ad611d Dimitris Aragiorgis
1231 a3ad611d Dimitris Aragiorgis
      if retries > 0:
1232 a3ad611d Dimitris Aragiorgis
        retries -= 1
1233 a3ad611d Dimitris Aragiorgis
1234 a3ad611d Dimitris Aragiorgis
    return False
1235 a3ad611d Dimitris Aragiorgis
1236 e5e20779 Faidon Liambotis
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1237 e5e20779 Faidon Liambotis
    """Waits for job changes.
1238 e5e20779 Faidon Liambotis

1239 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1240 e5e20779 Faidon Liambotis
    @param job_id: Job ID for which to wait
1241 a3ad611d Dimitris Aragiorgis
    @return: C{None} if no changes have been detected and a dict with two keys,
1242 a3ad611d Dimitris Aragiorgis
      C{job_info} and C{log_entries} otherwise.
1243 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1244 e5e20779 Faidon Liambotis

1245 e5e20779 Faidon Liambotis
    """
1246 e5e20779 Faidon Liambotis
    body = {
1247 e5e20779 Faidon Liambotis
      "fields": fields,
1248 e5e20779 Faidon Liambotis
      "previous_job_info": prev_job_info,
1249 e5e20779 Faidon Liambotis
      "previous_log_serial": prev_log_serial,
1250 e5e20779 Faidon Liambotis
      }
1251 e5e20779 Faidon Liambotis
1252 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1253 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1254 e5e20779 Faidon Liambotis
                             None, body)
1255 e5e20779 Faidon Liambotis
1256 e5e20779 Faidon Liambotis
  def CancelJob(self, job_id, dry_run=False):
1257 e5e20779 Faidon Liambotis
    """Cancels a job.
1258 e5e20779 Faidon Liambotis

1259 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1260 e5e20779 Faidon Liambotis
    @param job_id: id of the job to delete
1261 e5e20779 Faidon Liambotis
    @type dry_run: bool
1262 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1263 a3ad611d Dimitris Aragiorgis
    @rtype: tuple
1264 a3ad611d Dimitris Aragiorgis
    @return: tuple containing the result, and a message (bool, string)
1265 e5e20779 Faidon Liambotis

1266 e5e20779 Faidon Liambotis
    """
1267 e5e20779 Faidon Liambotis
    query = []
1268 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1269 e5e20779 Faidon Liambotis
1270 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1271 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1272 e5e20779 Faidon Liambotis
                             query, None)
1273 e5e20779 Faidon Liambotis
1274 e5e20779 Faidon Liambotis
  def GetNodes(self, bulk=False):
1275 e5e20779 Faidon Liambotis
    """Gets all nodes in the cluster.
1276 e5e20779 Faidon Liambotis

1277 e5e20779 Faidon Liambotis
    @type bulk: bool
1278 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
1279 e5e20779 Faidon Liambotis

1280 e5e20779 Faidon Liambotis
    @rtype: list of dict or str
1281 e5e20779 Faidon Liambotis
    @return: if bulk is true, info about nodes in the cluster,
1282 e5e20779 Faidon Liambotis
        else list of nodes in the cluster
1283 e5e20779 Faidon Liambotis

1284 e5e20779 Faidon Liambotis
    """
1285 e5e20779 Faidon Liambotis
    query = []
1286 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1287 e5e20779 Faidon Liambotis
1288 e5e20779 Faidon Liambotis
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1289 e5e20779 Faidon Liambotis
                              query, None)
1290 e5e20779 Faidon Liambotis
    if bulk:
1291 e5e20779 Faidon Liambotis
      return nodes
1292 e5e20779 Faidon Liambotis
    else:
1293 e5e20779 Faidon Liambotis
      return [n["id"] for n in nodes]
1294 e5e20779 Faidon Liambotis
1295 e5e20779 Faidon Liambotis
  def GetNode(self, node):
1296 e5e20779 Faidon Liambotis
    """Gets information about a node.
1297 e5e20779 Faidon Liambotis

1298 e5e20779 Faidon Liambotis
    @type node: str
1299 e5e20779 Faidon Liambotis
    @param node: node whose info to return
1300 e5e20779 Faidon Liambotis

1301 e5e20779 Faidon Liambotis
    @rtype: dict
1302 e5e20779 Faidon Liambotis
    @return: info about the node
1303 e5e20779 Faidon Liambotis

1304 e5e20779 Faidon Liambotis
    """
1305 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1306 e5e20779 Faidon Liambotis
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1307 e5e20779 Faidon Liambotis
                             None, None)
1308 e5e20779 Faidon Liambotis
1309 e5e20779 Faidon Liambotis
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1310 a3ad611d Dimitris Aragiorgis
                   dry_run=False, early_release=None,
1311 a3ad611d Dimitris Aragiorgis
                   mode=None, accept_old=False):
1312 e5e20779 Faidon Liambotis
    """Evacuates instances from a Ganeti node.
1313 e5e20779 Faidon Liambotis

1314 e5e20779 Faidon Liambotis
    @type node: str
1315 e5e20779 Faidon Liambotis
    @param node: node to evacuate
1316 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1317 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator to use
1318 e5e20779 Faidon Liambotis
    @type remote_node: str
1319 e5e20779 Faidon Liambotis
    @param remote_node: node to evaucate to
1320 e5e20779 Faidon Liambotis
    @type dry_run: bool
1321 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1322 e5e20779 Faidon Liambotis
    @type early_release: bool
1323 e5e20779 Faidon Liambotis
    @param early_release: whether to enable parallelization
1324 a3ad611d Dimitris Aragiorgis
    @type mode: string
1325 a3ad611d Dimitris Aragiorgis
    @param mode: Node evacuation mode
1326 a3ad611d Dimitris Aragiorgis
    @type accept_old: bool
1327 a3ad611d Dimitris Aragiorgis
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1328 a3ad611d Dimitris Aragiorgis
        results
1329 e5e20779 Faidon Liambotis

1330 a3ad611d Dimitris Aragiorgis
    @rtype: string, or a list for pre-2.5 results
1331 a3ad611d Dimitris Aragiorgis
    @return: Job ID or, if C{accept_old} is set and server is pre-2.5,
1332 a3ad611d Dimitris Aragiorgis
      list of (job ID, instance name, new secondary node); if dry_run was
1333 a3ad611d Dimitris Aragiorgis
      specified, then the actual move jobs were not submitted and the job IDs
1334 a3ad611d Dimitris Aragiorgis
      will be C{None}
1335 e5e20779 Faidon Liambotis

1336 e5e20779 Faidon Liambotis
    @raises GanetiApiError: if an iallocator and remote_node are both
1337 e5e20779 Faidon Liambotis
        specified
1338 e5e20779 Faidon Liambotis

1339 e5e20779 Faidon Liambotis
    """
1340 e5e20779 Faidon Liambotis
    if iallocator and remote_node:
1341 e5e20779 Faidon Liambotis
      raise GanetiApiError("Only one of iallocator or remote_node can be used")
1342 e5e20779 Faidon Liambotis
1343 e5e20779 Faidon Liambotis
    query = []
1344 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1345 a3ad611d Dimitris Aragiorgis
1346 a3ad611d Dimitris Aragiorgis
    if _NODE_EVAC_RES1 in self.GetFeatures():
1347 a3ad611d Dimitris Aragiorgis
      # Server supports body parameters
1348 a3ad611d Dimitris Aragiorgis
      body = {}
1349 a3ad611d Dimitris Aragiorgis
1350 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1351 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, remote_node is not None, "remote_node", remote_node)
1352 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, early_release is not None,
1353 a3ad611d Dimitris Aragiorgis
                 "early_release", early_release)
1354 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, mode is not None, "mode", mode)
1355 a3ad611d Dimitris Aragiorgis
    else:
1356 a3ad611d Dimitris Aragiorgis
      # Pre-2.5 request format
1357 a3ad611d Dimitris Aragiorgis
      body = None
1358 a3ad611d Dimitris Aragiorgis
1359 a3ad611d Dimitris Aragiorgis
      if not accept_old:
1360 a3ad611d Dimitris Aragiorgis
        raise GanetiApiError("Server is version 2.4 or earlier and caller does"
1361 a3ad611d Dimitris Aragiorgis
                             " not accept old-style results (parameter"
1362 a3ad611d Dimitris Aragiorgis
                             " accept_old)")
1363 a3ad611d Dimitris Aragiorgis
1364 a3ad611d Dimitris Aragiorgis
      # Pre-2.5 servers can only evacuate secondaries
1365 a3ad611d Dimitris Aragiorgis
      if mode is not None and mode != NODE_EVAC_SEC:
1366 a3ad611d Dimitris Aragiorgis
        raise GanetiApiError("Server can only evacuate secondary instances")
1367 a3ad611d Dimitris Aragiorgis
1368 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, iallocator, ("iallocator", iallocator))
1369 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, remote_node, ("remote_node", remote_node))
1370 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, early_release, ("early_release", 1))
1371 e5e20779 Faidon Liambotis
1372 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1373 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/evacuate" %
1374 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, node)), query, body)
1375 e5e20779 Faidon Liambotis
1376 a3ad611d Dimitris Aragiorgis
  def MigrateNode(self, node, mode=None, dry_run=False, iallocator=None,
1377 a3ad611d Dimitris Aragiorgis
                  target_node=None):
1378 e5e20779 Faidon Liambotis
    """Migrates all primary instances from a node.
1379 e5e20779 Faidon Liambotis

1380 e5e20779 Faidon Liambotis
    @type node: str
1381 e5e20779 Faidon Liambotis
    @param node: node to migrate
1382 e5e20779 Faidon Liambotis
    @type mode: string
1383 e5e20779 Faidon Liambotis
    @param mode: if passed, it will overwrite the live migration type,
1384 e5e20779 Faidon Liambotis
        otherwise the hypervisor default will be used
1385 e5e20779 Faidon Liambotis
    @type dry_run: bool
1386 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1387 a3ad611d Dimitris Aragiorgis
    @type iallocator: string
1388 a3ad611d Dimitris Aragiorgis
    @param iallocator: instance allocator to use
1389 a3ad611d Dimitris Aragiorgis
    @type target_node: string
1390 a3ad611d Dimitris Aragiorgis
    @param target_node: Target node for shared-storage instances
1391 e5e20779 Faidon Liambotis

1392 a3ad611d Dimitris Aragiorgis
    @rtype: string
1393 e5e20779 Faidon Liambotis
    @return: job id
1394 e5e20779 Faidon Liambotis

1395 e5e20779 Faidon Liambotis
    """
1396 e5e20779 Faidon Liambotis
    query = []
1397 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1398 e5e20779 Faidon Liambotis
1399 a3ad611d Dimitris Aragiorgis
    if _NODE_MIGRATE_REQV1 in self.GetFeatures():
1400 a3ad611d Dimitris Aragiorgis
      body = {}
1401 a3ad611d Dimitris Aragiorgis
1402 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, mode is not None, "mode", mode)
1403 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1404 a3ad611d Dimitris Aragiorgis
      _SetItemIf(body, target_node is not None, "target_node", target_node)
1405 a3ad611d Dimitris Aragiorgis
1406 a3ad611d Dimitris Aragiorgis
      assert len(query) <= 1
1407 a3ad611d Dimitris Aragiorgis
1408 a3ad611d Dimitris Aragiorgis
      return self._SendRequest(HTTP_POST,
1409 a3ad611d Dimitris Aragiorgis
                               ("/%s/nodes/%s/migrate" %
1410 a3ad611d Dimitris Aragiorgis
                                (GANETI_RAPI_VERSION, node)), query, body)
1411 a3ad611d Dimitris Aragiorgis
    else:
1412 a3ad611d Dimitris Aragiorgis
      # Use old request format
1413 a3ad611d Dimitris Aragiorgis
      if target_node is not None:
1414 a3ad611d Dimitris Aragiorgis
        raise GanetiApiError("Server does not support specifying target node"
1415 a3ad611d Dimitris Aragiorgis
                             " for node migration")
1416 a3ad611d Dimitris Aragiorgis
1417 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, mode is not None, ("mode", mode))
1418 a3ad611d Dimitris Aragiorgis
1419 a3ad611d Dimitris Aragiorgis
      return self._SendRequest(HTTP_POST,
1420 a3ad611d Dimitris Aragiorgis
                               ("/%s/nodes/%s/migrate" %
1421 a3ad611d Dimitris Aragiorgis
                                (GANETI_RAPI_VERSION, node)), query, None)
1422 e5e20779 Faidon Liambotis
1423 e5e20779 Faidon Liambotis
  def GetNodeRole(self, node):
1424 e5e20779 Faidon Liambotis
    """Gets the current role for a node.
1425 e5e20779 Faidon Liambotis

1426 e5e20779 Faidon Liambotis
    @type node: str
1427 e5e20779 Faidon Liambotis
    @param node: node whose role to return
1428 e5e20779 Faidon Liambotis

1429 e5e20779 Faidon Liambotis
    @rtype: str
1430 e5e20779 Faidon Liambotis
    @return: the current role for a node
1431 e5e20779 Faidon Liambotis

1432 e5e20779 Faidon Liambotis
    """
1433 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1434 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1435 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1436 e5e20779 Faidon Liambotis
1437 a3ad611d Dimitris Aragiorgis
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1438 e5e20779 Faidon Liambotis
    """Sets the role for a node.
1439 e5e20779 Faidon Liambotis

1440 e5e20779 Faidon Liambotis
    @type node: str
1441 e5e20779 Faidon Liambotis
    @param node: the node whose role to set
1442 e5e20779 Faidon Liambotis
    @type role: str
1443 e5e20779 Faidon Liambotis
    @param role: the role to set for the node
1444 e5e20779 Faidon Liambotis
    @type force: bool
1445 e5e20779 Faidon Liambotis
    @param force: whether to force the role change
1446 a3ad611d Dimitris Aragiorgis
    @type auto_promote: bool
1447 a3ad611d Dimitris Aragiorgis
    @param auto_promote: Whether node(s) should be promoted to master candidate
1448 a3ad611d Dimitris Aragiorgis
                         if necessary
1449 e5e20779 Faidon Liambotis

1450 a3ad611d Dimitris Aragiorgis
    @rtype: string
1451 e5e20779 Faidon Liambotis
    @return: job id
1452 e5e20779 Faidon Liambotis

1453 e5e20779 Faidon Liambotis
    """
1454 a3ad611d Dimitris Aragiorgis
    query = []
1455 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1456 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1457 e5e20779 Faidon Liambotis
1458 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1459 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1460 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, role)
1461 e5e20779 Faidon Liambotis
1462 a3ad611d Dimitris Aragiorgis
  def PowercycleNode(self, node, force=False):
1463 a3ad611d Dimitris Aragiorgis
    """Powercycles a node.
1464 a3ad611d Dimitris Aragiorgis

1465 a3ad611d Dimitris Aragiorgis
    @type node: string
1466 a3ad611d Dimitris Aragiorgis
    @param node: Node name
1467 a3ad611d Dimitris Aragiorgis
    @type force: bool
1468 a3ad611d Dimitris Aragiorgis
    @param force: Whether to force the operation
1469 a3ad611d Dimitris Aragiorgis
    @rtype: string
1470 a3ad611d Dimitris Aragiorgis
    @return: job id
1471 a3ad611d Dimitris Aragiorgis

1472 a3ad611d Dimitris Aragiorgis
    """
1473 a3ad611d Dimitris Aragiorgis
    query = []
1474 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1475 a3ad611d Dimitris Aragiorgis
1476 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
1477 a3ad611d Dimitris Aragiorgis
                             ("/%s/nodes/%s/powercycle" %
1478 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, node)), query, None)
1479 a3ad611d Dimitris Aragiorgis
1480 a3ad611d Dimitris Aragiorgis
  def ModifyNode(self, node, **kwargs):
1481 a3ad611d Dimitris Aragiorgis
    """Modifies a node.
1482 a3ad611d Dimitris Aragiorgis

1483 a3ad611d Dimitris Aragiorgis
    More details for parameters can be found in the RAPI documentation.
1484 a3ad611d Dimitris Aragiorgis

1485 a3ad611d Dimitris Aragiorgis
    @type node: string
1486 a3ad611d Dimitris Aragiorgis
    @param node: Node name
1487 a3ad611d Dimitris Aragiorgis
    @rtype: string
1488 a3ad611d Dimitris Aragiorgis
    @return: job id
1489 a3ad611d Dimitris Aragiorgis

1490 a3ad611d Dimitris Aragiorgis
    """
1491 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
1492 a3ad611d Dimitris Aragiorgis
                             ("/%s/nodes/%s/modify" %
1493 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1494 a3ad611d Dimitris Aragiorgis
1495 e5e20779 Faidon Liambotis
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1496 e5e20779 Faidon Liambotis
    """Gets the storage units for a node.
1497 e5e20779 Faidon Liambotis

1498 e5e20779 Faidon Liambotis
    @type node: str
1499 e5e20779 Faidon Liambotis
    @param node: the node whose storage units to return
1500 e5e20779 Faidon Liambotis
    @type storage_type: str
1501 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to return
1502 e5e20779 Faidon Liambotis
    @type output_fields: str
1503 e5e20779 Faidon Liambotis
    @param output_fields: storage type fields to return
1504 e5e20779 Faidon Liambotis

1505 a3ad611d Dimitris Aragiorgis
    @rtype: string
1506 e5e20779 Faidon Liambotis
    @return: job id where results can be retrieved
1507 e5e20779 Faidon Liambotis

1508 e5e20779 Faidon Liambotis
    """
1509 e5e20779 Faidon Liambotis
    query = [
1510 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1511 e5e20779 Faidon Liambotis
      ("output_fields", output_fields),
1512 e5e20779 Faidon Liambotis
      ]
1513 e5e20779 Faidon Liambotis
1514 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1515 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage" %
1516 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1517 e5e20779 Faidon Liambotis
1518 e5e20779 Faidon Liambotis
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1519 e5e20779 Faidon Liambotis
    """Modifies parameters of storage units on the node.
1520 e5e20779 Faidon Liambotis

1521 e5e20779 Faidon Liambotis
    @type node: str
1522 e5e20779 Faidon Liambotis
    @param node: node whose storage units to modify
1523 e5e20779 Faidon Liambotis
    @type storage_type: str
1524 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to modify
1525 e5e20779 Faidon Liambotis
    @type name: str
1526 e5e20779 Faidon Liambotis
    @param name: name of the storage unit
1527 e5e20779 Faidon Liambotis
    @type allocatable: bool or None
1528 e5e20779 Faidon Liambotis
    @param allocatable: Whether to set the "allocatable" flag on the storage
1529 e5e20779 Faidon Liambotis
                        unit (None=no modification, True=set, False=unset)
1530 e5e20779 Faidon Liambotis

1531 a3ad611d Dimitris Aragiorgis
    @rtype: string
1532 e5e20779 Faidon Liambotis
    @return: job id
1533 e5e20779 Faidon Liambotis

1534 e5e20779 Faidon Liambotis
    """
1535 e5e20779 Faidon Liambotis
    query = [
1536 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1537 e5e20779 Faidon Liambotis
      ("name", name),
1538 e5e20779 Faidon Liambotis
      ]
1539 e5e20779 Faidon Liambotis
1540 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1541 e5e20779 Faidon Liambotis
1542 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1543 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/modify" %
1544 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1545 e5e20779 Faidon Liambotis
1546 e5e20779 Faidon Liambotis
  def RepairNodeStorageUnits(self, node, storage_type, name):
1547 e5e20779 Faidon Liambotis
    """Repairs a storage unit on the node.
1548 e5e20779 Faidon Liambotis

1549 e5e20779 Faidon Liambotis
    @type node: str
1550 e5e20779 Faidon Liambotis
    @param node: node whose storage units to repair
1551 e5e20779 Faidon Liambotis
    @type storage_type: str
1552 e5e20779 Faidon Liambotis
    @param storage_type: storage type to repair
1553 e5e20779 Faidon Liambotis
    @type name: str
1554 e5e20779 Faidon Liambotis
    @param name: name of the storage unit to repair
1555 e5e20779 Faidon Liambotis

1556 a3ad611d Dimitris Aragiorgis
    @rtype: string
1557 e5e20779 Faidon Liambotis
    @return: job id
1558 e5e20779 Faidon Liambotis

1559 e5e20779 Faidon Liambotis
    """
1560 e5e20779 Faidon Liambotis
    query = [
1561 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1562 e5e20779 Faidon Liambotis
      ("name", name),
1563 e5e20779 Faidon Liambotis
      ]
1564 e5e20779 Faidon Liambotis
1565 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1566 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/repair" %
1567 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1568 e5e20779 Faidon Liambotis
1569 e5e20779 Faidon Liambotis
  def GetNodeTags(self, node):
1570 e5e20779 Faidon Liambotis
    """Gets the tags for a node.
1571 e5e20779 Faidon Liambotis

1572 e5e20779 Faidon Liambotis
    @type node: str
1573 e5e20779 Faidon Liambotis
    @param node: node whose tags to return
1574 e5e20779 Faidon Liambotis

1575 e5e20779 Faidon Liambotis
    @rtype: list of str
1576 e5e20779 Faidon Liambotis
    @return: tags for the node
1577 e5e20779 Faidon Liambotis

1578 e5e20779 Faidon Liambotis
    """
1579 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1580 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1581 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1582 e5e20779 Faidon Liambotis
1583 e5e20779 Faidon Liambotis
  def AddNodeTags(self, node, tags, dry_run=False):
1584 e5e20779 Faidon Liambotis
    """Adds tags to a node.
1585 e5e20779 Faidon Liambotis

1586 e5e20779 Faidon Liambotis
    @type node: str
1587 e5e20779 Faidon Liambotis
    @param node: node to add tags to
1588 e5e20779 Faidon Liambotis
    @type tags: list of str
1589 e5e20779 Faidon Liambotis
    @param tags: tags to add to the node
1590 e5e20779 Faidon Liambotis
    @type dry_run: bool
1591 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1592 e5e20779 Faidon Liambotis

1593 a3ad611d Dimitris Aragiorgis
    @rtype: string
1594 e5e20779 Faidon Liambotis
    @return: job id
1595 e5e20779 Faidon Liambotis

1596 e5e20779 Faidon Liambotis
    """
1597 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1598 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1599 e5e20779 Faidon Liambotis
1600 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1601 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1602 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, tags)
1603 e5e20779 Faidon Liambotis
1604 e5e20779 Faidon Liambotis
  def DeleteNodeTags(self, node, tags, dry_run=False):
1605 e5e20779 Faidon Liambotis
    """Delete tags from a node.
1606 e5e20779 Faidon Liambotis

1607 e5e20779 Faidon Liambotis
    @type node: str
1608 e5e20779 Faidon Liambotis
    @param node: node to remove tags from
1609 e5e20779 Faidon Liambotis
    @type tags: list of str
1610 e5e20779 Faidon Liambotis
    @param tags: tags to remove from the node
1611 e5e20779 Faidon Liambotis
    @type dry_run: bool
1612 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1613 e5e20779 Faidon Liambotis

1614 a3ad611d Dimitris Aragiorgis
    @rtype: string
1615 e5e20779 Faidon Liambotis
    @return: job id
1616 e5e20779 Faidon Liambotis

1617 e5e20779 Faidon Liambotis
    """
1618 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1619 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1620 e5e20779 Faidon Liambotis
1621 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1622 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1623 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1624 067dda99 Vangelis Koukis
1625 a3ad611d Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1626 a3ad611d Dimitris Aragiorgis
    """Gets all networks in the cluster.
1627 a3ad611d Dimitris Aragiorgis

1628 a3ad611d Dimitris Aragiorgis
    @type bulk: bool
1629 a3ad611d Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1630 a3ad611d Dimitris Aragiorgis

1631 a3ad611d Dimitris Aragiorgis
    @rtype: list of dict or str
1632 a3ad611d Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1633 a3ad611d Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1634 a3ad611d Dimitris Aragiorgis

1635 a3ad611d Dimitris Aragiorgis
    """
1636 a3ad611d Dimitris Aragiorgis
    query = []
1637 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1638 a3ad611d Dimitris Aragiorgis
1639 a3ad611d Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1640 a3ad611d Dimitris Aragiorgis
                               query, None)
1641 a3ad611d Dimitris Aragiorgis
    if bulk:
1642 a3ad611d Dimitris Aragiorgis
      return networks
1643 a3ad611d Dimitris Aragiorgis
    else:
1644 a3ad611d Dimitris Aragiorgis
      return [n["name"] for n in networks]
1645 a3ad611d Dimitris Aragiorgis
1646 a3ad611d Dimitris Aragiorgis
  def GetNetwork(self, network):
1647 a3ad611d Dimitris Aragiorgis
    """Gets information about a network.
1648 a3ad611d Dimitris Aragiorgis

1649 a3ad611d Dimitris Aragiorgis
    @type group: str
1650 a3ad611d Dimitris Aragiorgis
    @param group: name of the network whose info to return
1651 a3ad611d Dimitris Aragiorgis

1652 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1653 a3ad611d Dimitris Aragiorgis
    @return: info about the network
1654 a3ad611d Dimitris Aragiorgis

1655 a3ad611d Dimitris Aragiorgis
    """
1656 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1657 a3ad611d Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1658 a3ad611d Dimitris Aragiorgis
                             None, None)
1659 a3ad611d Dimitris Aragiorgis
1660 a3ad611d Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1661 9ef880a4 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None, network_type=None,
1662 db2eb7c8 Dimitris Aragiorgis
                    tags=None, dry_run=False):
1663 a3ad611d Dimitris Aragiorgis
    """Creates a new network.
1664 a3ad611d Dimitris Aragiorgis

1665 a3ad611d Dimitris Aragiorgis
    @type name: str
1666 a3ad611d Dimitris Aragiorgis
    @param name: the name of network to create
1667 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1668 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1669 a3ad611d Dimitris Aragiorgis

1670 a3ad611d Dimitris Aragiorgis
    @rtype: string
1671 a3ad611d Dimitris Aragiorgis
    @return: job id
1672 a3ad611d Dimitris Aragiorgis

1673 a3ad611d Dimitris Aragiorgis
    """
1674 a3ad611d Dimitris Aragiorgis
    query = []
1675 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1676 a3ad611d Dimitris Aragiorgis
1677 9ef880a4 Dimitris Aragiorgis
    if tags:
1678 9ef880a4 Dimitris Aragiorgis
      tags = tags.split(',')
1679 71ec054d Christos Stavrakakis
    else:
1680 71ec054d Christos Stavrakakis
      tags = []
1681 71ec054d Christos Stavrakakis
1682 78548bed Christos Stavrakakis
1683 a3ad611d Dimitris Aragiorgis
    body = {
1684 a3ad611d Dimitris Aragiorgis
      "network_name": network_name,
1685 a3ad611d Dimitris Aragiorgis
      "gateway": gateway,
1686 a3ad611d Dimitris Aragiorgis
      "network": network,
1687 a3ad611d Dimitris Aragiorgis
      "gateway6": gateway6,
1688 a3ad611d Dimitris Aragiorgis
      "network6": network6,
1689 a3ad611d Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1690 9ef880a4 Dimitris Aragiorgis
      "network_type": network_type,
1691 9ef880a4 Dimitris Aragiorgis
      "tags": tags
1692 a3ad611d Dimitris Aragiorgis
      }
1693 a3ad611d Dimitris Aragiorgis
1694 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1695 a3ad611d Dimitris Aragiorgis
                             query, body)
1696 a3ad611d Dimitris Aragiorgis
1697 a3ad611d Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link, depends=None):
1698 a3ad611d Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1699 a3ad611d Dimitris Aragiorgis

1700 a3ad611d Dimitris Aragiorgis
    """
1701 a3ad611d Dimitris Aragiorgis
    body = {
1702 a3ad611d Dimitris Aragiorgis
      "group_name": group_name,
1703 a3ad611d Dimitris Aragiorgis
      "network_mode": mode,
1704 a3ad611d Dimitris Aragiorgis
      "network_link": link
1705 a3ad611d Dimitris Aragiorgis
      }
1706 a3ad611d Dimitris Aragiorgis
1707 a3ad611d Dimitris Aragiorgis
    if depends:
1708 a3ad611d Dimitris Aragiorgis
      body['depends'] = []
1709 a3ad611d Dimitris Aragiorgis
      for d in depends:
1710 a3ad611d Dimitris Aragiorgis
        body['depends'].append([d, ["success"]])
1711 a3ad611d Dimitris Aragiorgis
1712 a3ad611d Dimitris Aragiorgis
1713 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1714 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1715 a3ad611d Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), None, body)
1716 a3ad611d Dimitris Aragiorgis
1717 a3ad611d Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name, depends=None):
1718 a3ad611d Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1719 a3ad611d Dimitris Aragiorgis

1720 a3ad611d Dimitris Aragiorgis
    """
1721 a3ad611d Dimitris Aragiorgis
    body = {
1722 a3ad611d Dimitris Aragiorgis
      "group_name": group_name
1723 a3ad611d Dimitris Aragiorgis
      }
1724 a3ad611d Dimitris Aragiorgis
1725 a3ad611d Dimitris Aragiorgis
    if depends:
1726 a3ad611d Dimitris Aragiorgis
      body['depends'] = []
1727 a3ad611d Dimitris Aragiorgis
      for d in depends:
1728 a3ad611d Dimitris Aragiorgis
        body['depends'].append([d, ["success"]])
1729 a3ad611d Dimitris Aragiorgis
1730 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1731 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1732 a3ad611d Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), None, body)
1733 a3ad611d Dimitris Aragiorgis
1734 a3ad611d Dimitris Aragiorgis
1735 a3ad611d Dimitris Aragiorgis
  def DeleteNetwork(self, network, depends=None):
1736 a3ad611d Dimitris Aragiorgis
    """Deletes a network.
1737 a3ad611d Dimitris Aragiorgis

1738 a3ad611d Dimitris Aragiorgis
    @type group: str
1739 a3ad611d Dimitris Aragiorgis
    @param group: the network to delete
1740 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1741 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1742 a3ad611d Dimitris Aragiorgis

1743 a3ad611d Dimitris Aragiorgis
    @rtype: string
1744 a3ad611d Dimitris Aragiorgis
    @return: job id
1745 a3ad611d Dimitris Aragiorgis

1746 a3ad611d Dimitris Aragiorgis
    """
1747 a3ad611d Dimitris Aragiorgis
    body = {}
1748 a3ad611d Dimitris Aragiorgis
    if depends:
1749 a3ad611d Dimitris Aragiorgis
      body['depends'] = []
1750 a3ad611d Dimitris Aragiorgis
      for d in depends:
1751 a3ad611d Dimitris Aragiorgis
        body['depends'].append([d, ["success"]])
1752 a3ad611d Dimitris Aragiorgis
1753 a3ad611d Dimitris Aragiorgis
1754 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1755 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1756 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, body)
1757 a3ad611d Dimitris Aragiorgis
1758 067dda99 Vangelis Koukis
  def GetGroups(self, bulk=False):
1759 067dda99 Vangelis Koukis
    """Gets all node groups in the cluster.
1760 067dda99 Vangelis Koukis

1761 067dda99 Vangelis Koukis
    @type bulk: bool
1762 067dda99 Vangelis Koukis
    @param bulk: whether to return all information about the groups
1763 067dda99 Vangelis Koukis

1764 067dda99 Vangelis Koukis
    @rtype: list of dict or str
1765 067dda99 Vangelis Koukis
    @return: if bulk is true, a list of dictionaries with info about all node
1766 067dda99 Vangelis Koukis
        groups in the cluster, else a list of names of those node groups
1767 067dda99 Vangelis Koukis

1768 067dda99 Vangelis Koukis
    """
1769 067dda99 Vangelis Koukis
    query = []
1770 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1771 067dda99 Vangelis Koukis
1772 067dda99 Vangelis Koukis
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1773 067dda99 Vangelis Koukis
                               query, None)
1774 067dda99 Vangelis Koukis
    if bulk:
1775 067dda99 Vangelis Koukis
      return groups
1776 067dda99 Vangelis Koukis
    else:
1777 067dda99 Vangelis Koukis
      return [g["name"] for g in groups]
1778 067dda99 Vangelis Koukis
1779 067dda99 Vangelis Koukis
  def GetGroup(self, group):
1780 067dda99 Vangelis Koukis
    """Gets information about a node group.
1781 067dda99 Vangelis Koukis

1782 067dda99 Vangelis Koukis
    @type group: str
1783 067dda99 Vangelis Koukis
    @param group: name of the node group whose info to return
1784 067dda99 Vangelis Koukis

1785 067dda99 Vangelis Koukis
    @rtype: dict
1786 067dda99 Vangelis Koukis
    @return: info about the node group
1787 067dda99 Vangelis Koukis

1788 067dda99 Vangelis Koukis
    """
1789 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1790 067dda99 Vangelis Koukis
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1791 067dda99 Vangelis Koukis
                             None, None)
1792 067dda99 Vangelis Koukis
1793 067dda99 Vangelis Koukis
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1794 067dda99 Vangelis Koukis
    """Creates a new node group.
1795 067dda99 Vangelis Koukis

1796 067dda99 Vangelis Koukis
    @type name: str
1797 067dda99 Vangelis Koukis
    @param name: the name of node group to create
1798 067dda99 Vangelis Koukis
    @type alloc_policy: str
1799 067dda99 Vangelis Koukis
    @param alloc_policy: the desired allocation policy for the group, if any
1800 067dda99 Vangelis Koukis
    @type dry_run: bool
1801 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1802 067dda99 Vangelis Koukis

1803 a3ad611d Dimitris Aragiorgis
    @rtype: string
1804 067dda99 Vangelis Koukis
    @return: job id
1805 067dda99 Vangelis Koukis

1806 067dda99 Vangelis Koukis
    """
1807 067dda99 Vangelis Koukis
    query = []
1808 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1809 067dda99 Vangelis Koukis
1810 067dda99 Vangelis Koukis
    body = {
1811 067dda99 Vangelis Koukis
      "name": name,
1812 067dda99 Vangelis Koukis
      "alloc_policy": alloc_policy
1813 067dda99 Vangelis Koukis
      }
1814 067dda99 Vangelis Koukis
1815 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1816 067dda99 Vangelis Koukis
                             query, body)
1817 067dda99 Vangelis Koukis
1818 067dda99 Vangelis Koukis
  def ModifyGroup(self, group, **kwargs):
1819 067dda99 Vangelis Koukis
    """Modifies a node group.
1820 067dda99 Vangelis Koukis

1821 067dda99 Vangelis Koukis
    More details for parameters can be found in the RAPI documentation.
1822 067dda99 Vangelis Koukis

1823 067dda99 Vangelis Koukis
    @type group: string
1824 067dda99 Vangelis Koukis
    @param group: Node group name
1825 a3ad611d Dimitris Aragiorgis
    @rtype: string
1826 067dda99 Vangelis Koukis
    @return: job id
1827 067dda99 Vangelis Koukis

1828 067dda99 Vangelis Koukis
    """
1829 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1830 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/modify" %
1831 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1832 067dda99 Vangelis Koukis
1833 067dda99 Vangelis Koukis
  def DeleteGroup(self, group, dry_run=False):
1834 067dda99 Vangelis Koukis
    """Deletes a node group.
1835 067dda99 Vangelis Koukis

1836 067dda99 Vangelis Koukis
    @type group: str
1837 067dda99 Vangelis Koukis
    @param group: the node group to delete
1838 067dda99 Vangelis Koukis
    @type dry_run: bool
1839 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1840 067dda99 Vangelis Koukis

1841 a3ad611d Dimitris Aragiorgis
    @rtype: string
1842 067dda99 Vangelis Koukis
    @return: job id
1843 067dda99 Vangelis Koukis

1844 067dda99 Vangelis Koukis
    """
1845 067dda99 Vangelis Koukis
    query = []
1846 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1847 067dda99 Vangelis Koukis
1848 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_DELETE,
1849 067dda99 Vangelis Koukis
                             ("/%s/groups/%s" %
1850 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), query, None)
1851 067dda99 Vangelis Koukis
1852 067dda99 Vangelis Koukis
  def RenameGroup(self, group, new_name):
1853 067dda99 Vangelis Koukis
    """Changes the name of a node group.
1854 067dda99 Vangelis Koukis

1855 067dda99 Vangelis Koukis
    @type group: string
1856 067dda99 Vangelis Koukis
    @param group: Node group name
1857 067dda99 Vangelis Koukis
    @type new_name: string
1858 067dda99 Vangelis Koukis
    @param new_name: New node group name
1859 067dda99 Vangelis Koukis

1860 a3ad611d Dimitris Aragiorgis
    @rtype: string
1861 067dda99 Vangelis Koukis
    @return: job id
1862 067dda99 Vangelis Koukis

1863 067dda99 Vangelis Koukis
    """
1864 067dda99 Vangelis Koukis
    body = {
1865 067dda99 Vangelis Koukis
      "new_name": new_name,
1866 067dda99 Vangelis Koukis
      }
1867 067dda99 Vangelis Koukis
1868 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1869 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/rename" %
1870 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, body)
1871 067dda99 Vangelis Koukis
1872 067dda99 Vangelis Koukis
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
1873 067dda99 Vangelis Koukis
    """Assigns nodes to a group.
1874 067dda99 Vangelis Koukis

1875 067dda99 Vangelis Koukis
    @type group: string
1876 067dda99 Vangelis Koukis
    @param group: Node gropu name
1877 067dda99 Vangelis Koukis
    @type nodes: list of strings
1878 067dda99 Vangelis Koukis
    @param nodes: List of nodes to assign to the group
1879 067dda99 Vangelis Koukis

1880 a3ad611d Dimitris Aragiorgis
    @rtype: string
1881 067dda99 Vangelis Koukis
    @return: job id
1882 067dda99 Vangelis Koukis

1883 067dda99 Vangelis Koukis
    """
1884 067dda99 Vangelis Koukis
    query = []
1885 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1886 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1887 067dda99 Vangelis Koukis
1888 067dda99 Vangelis Koukis
    body = {
1889 067dda99 Vangelis Koukis
      "nodes": nodes,
1890 067dda99 Vangelis Koukis
      }
1891 067dda99 Vangelis Koukis
1892 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1893 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/assign-nodes" %
1894 067dda99 Vangelis Koukis
                             (GANETI_RAPI_VERSION, group)), query, body)
1895 a3ad611d Dimitris Aragiorgis
1896 a3ad611d Dimitris Aragiorgis
  def GetGroupTags(self, group):
1897 a3ad611d Dimitris Aragiorgis
    """Gets tags for a node group.
1898 a3ad611d Dimitris Aragiorgis

1899 a3ad611d Dimitris Aragiorgis
    @type group: string
1900 a3ad611d Dimitris Aragiorgis
    @param group: Node group whose tags to return
1901 a3ad611d Dimitris Aragiorgis

1902 a3ad611d Dimitris Aragiorgis
    @rtype: list of strings
1903 a3ad611d Dimitris Aragiorgis
    @return: tags for the group
1904 a3ad611d Dimitris Aragiorgis

1905 a3ad611d Dimitris Aragiorgis
    """
1906 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1907 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
1908 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), None, None)
1909 a3ad611d Dimitris Aragiorgis
1910 a3ad611d Dimitris Aragiorgis
  def AddGroupTags(self, group, tags, dry_run=False):
1911 a3ad611d Dimitris Aragiorgis
    """Adds tags to a node group.
1912 a3ad611d Dimitris Aragiorgis

1913 a3ad611d Dimitris Aragiorgis
    @type group: str
1914 a3ad611d Dimitris Aragiorgis
    @param group: group to add tags to
1915 a3ad611d Dimitris Aragiorgis
    @type tags: list of string
1916 a3ad611d Dimitris Aragiorgis
    @param tags: tags to add to the group
1917 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1918 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1919 a3ad611d Dimitris Aragiorgis

1920 a3ad611d Dimitris Aragiorgis
    @rtype: string
1921 a3ad611d Dimitris Aragiorgis
    @return: job id
1922 a3ad611d Dimitris Aragiorgis

1923 a3ad611d Dimitris Aragiorgis
    """
1924 a3ad611d Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1925 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1926 a3ad611d Dimitris Aragiorgis
1927 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1928 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
1929 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), query, None)
1930 a3ad611d Dimitris Aragiorgis
1931 a3ad611d Dimitris Aragiorgis
  def DeleteGroupTags(self, group, tags, dry_run=False):
1932 a3ad611d Dimitris Aragiorgis
    """Deletes tags from a node group.
1933 a3ad611d Dimitris Aragiorgis

1934 a3ad611d Dimitris Aragiorgis
    @type group: str
1935 a3ad611d Dimitris Aragiorgis
    @param group: group to delete tags from
1936 a3ad611d Dimitris Aragiorgis
    @type tags: list of string
1937 a3ad611d Dimitris Aragiorgis
    @param tags: tags to delete
1938 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1939 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1940 a3ad611d Dimitris Aragiorgis
    @rtype: string
1941 a3ad611d Dimitris Aragiorgis
    @return: job id
1942 a3ad611d Dimitris Aragiorgis

1943 a3ad611d Dimitris Aragiorgis
    """
1944 a3ad611d Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1945 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1946 a3ad611d Dimitris Aragiorgis
1947 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1948 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
1949 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), query, None)
1950 a3ad611d Dimitris Aragiorgis
1951 a3ad611d Dimitris Aragiorgis
  def Query(self, what, fields, qfilter=None):
1952 a3ad611d Dimitris Aragiorgis
    """Retrieves information about resources.
1953 a3ad611d Dimitris Aragiorgis

1954 a3ad611d Dimitris Aragiorgis
    @type what: string
1955 a3ad611d Dimitris Aragiorgis
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1956 a3ad611d Dimitris Aragiorgis
    @type fields: list of string
1957 a3ad611d Dimitris Aragiorgis
    @param fields: Requested fields
1958 a3ad611d Dimitris Aragiorgis
    @type qfilter: None or list
1959 a3ad611d Dimitris Aragiorgis
    @param qfilter: Query filter
1960 a3ad611d Dimitris Aragiorgis

1961 a3ad611d Dimitris Aragiorgis
    @rtype: string
1962 a3ad611d Dimitris Aragiorgis
    @return: job id
1963 a3ad611d Dimitris Aragiorgis

1964 a3ad611d Dimitris Aragiorgis
    """
1965 a3ad611d Dimitris Aragiorgis
    body = {
1966 a3ad611d Dimitris Aragiorgis
      "fields": fields,
1967 a3ad611d Dimitris Aragiorgis
      }
1968 a3ad611d Dimitris Aragiorgis
1969 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
1970 a3ad611d Dimitris Aragiorgis
    # TODO: remove "filter" after 2.7
1971 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
1972 a3ad611d Dimitris Aragiorgis
1973 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1974 a3ad611d Dimitris Aragiorgis
                             ("/%s/query/%s" %
1975 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, what)), None, body)
1976 a3ad611d Dimitris Aragiorgis
1977 a3ad611d Dimitris Aragiorgis
  def QueryFields(self, what, fields=None):
1978 a3ad611d Dimitris Aragiorgis
    """Retrieves available fields for a resource.
1979 a3ad611d Dimitris Aragiorgis

1980 a3ad611d Dimitris Aragiorgis
    @type what: string
1981 a3ad611d Dimitris Aragiorgis
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
1982 a3ad611d Dimitris Aragiorgis
    @type fields: list of string
1983 a3ad611d Dimitris Aragiorgis
    @param fields: Requested fields
1984 a3ad611d Dimitris Aragiorgis

1985 a3ad611d Dimitris Aragiorgis
    @rtype: string
1986 a3ad611d Dimitris Aragiorgis
    @return: job id
1987 a3ad611d Dimitris Aragiorgis

1988 a3ad611d Dimitris Aragiorgis
    """
1989 a3ad611d Dimitris Aragiorgis
    query = []
1990 a3ad611d Dimitris Aragiorgis
1991 a3ad611d Dimitris Aragiorgis
    if fields is not None:
1992 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, True, ("fields", ",".join(fields)))
1993 a3ad611d Dimitris Aragiorgis
1994 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1995 a3ad611d Dimitris Aragiorgis
                             ("/%s/query/%s/fields" %
1996 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, what)), query, None)