Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (60.2 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 aee560b0 Christos Stavrakakis
#: Not enough resources (iallocator failure, disk space, memory, etc.)
116 aee560b0 Christos Stavrakakis
ECODE_NORES = "insufficient_resources"
117 aee560b0 Christos Stavrakakis
118 aee560b0 Christos Stavrakakis
#: Temporarily out of resources; operation can be tried again
119 aee560b0 Christos Stavrakakis
ECODE_TEMP_NORES = "temp_insufficient_resources"
120 aee560b0 Christos Stavrakakis
121 e5e20779 Faidon Liambotis
# Older pycURL versions don't have all error constants
122 e5e20779 Faidon Liambotis
try:
123 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT = pycurl.E_SSL_CACERT
124 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE = pycurl.E_SSL_CACERT_BADFILE
125 e5e20779 Faidon Liambotis
except AttributeError:
126 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT = 60
127 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE = 77
128 e5e20779 Faidon Liambotis
129 e5e20779 Faidon Liambotis
_CURL_SSL_CERT_ERRORS = frozenset([
130 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT,
131 e5e20779 Faidon Liambotis
  _CURLE_SSL_CACERT_BADFILE,
132 e5e20779 Faidon Liambotis
  ])
133 e5e20779 Faidon Liambotis
134 e5e20779 Faidon Liambotis
135 e5e20779 Faidon Liambotis
class Error(Exception):
136 e5e20779 Faidon Liambotis
  """Base error class for this module.
137 e5e20779 Faidon Liambotis

138 e5e20779 Faidon Liambotis
  """
139 e5e20779 Faidon Liambotis
  pass
140 e5e20779 Faidon Liambotis
141 e5e20779 Faidon Liambotis
142 a3ad611d Dimitris Aragiorgis
class GanetiApiError(Error):
143 a3ad611d Dimitris Aragiorgis
  """Generic error raised from Ganeti API.
144 a3ad611d Dimitris Aragiorgis

145 a3ad611d Dimitris Aragiorgis
  """
146 a3ad611d Dimitris Aragiorgis
  def __init__(self, msg, code=None):
147 a3ad611d Dimitris Aragiorgis
    Error.__init__(self, msg)
148 a3ad611d Dimitris Aragiorgis
    self.code = code
149 a3ad611d Dimitris Aragiorgis
150 a3ad611d Dimitris Aragiorgis
151 a3ad611d Dimitris Aragiorgis
class CertificateError(GanetiApiError):
152 e5e20779 Faidon Liambotis
  """Raised when a problem is found with the SSL certificate.
153 e5e20779 Faidon Liambotis

154 e5e20779 Faidon Liambotis
  """
155 e5e20779 Faidon Liambotis
  pass
156 e5e20779 Faidon Liambotis
157 e5e20779 Faidon Liambotis
158 a3ad611d Dimitris Aragiorgis
def _AppendIf(container, condition, value):
159 a3ad611d Dimitris Aragiorgis
  """Appends to a list if a condition evaluates to truth.
160 e5e20779 Faidon Liambotis

161 e5e20779 Faidon Liambotis
  """
162 a3ad611d Dimitris Aragiorgis
  if condition:
163 a3ad611d Dimitris Aragiorgis
    container.append(value)
164 a3ad611d Dimitris Aragiorgis
165 a3ad611d Dimitris Aragiorgis
  return condition
166 a3ad611d Dimitris Aragiorgis
167 a3ad611d Dimitris Aragiorgis
168 a3ad611d Dimitris Aragiorgis
def _AppendDryRunIf(container, condition):
169 a3ad611d Dimitris Aragiorgis
  """Appends a "dry-run" parameter if a condition evaluates to truth.
170 a3ad611d Dimitris Aragiorgis

171 a3ad611d Dimitris Aragiorgis
  """
172 a3ad611d Dimitris Aragiorgis
  return _AppendIf(container, condition, (_QPARAM_DRY_RUN, 1))
173 a3ad611d Dimitris Aragiorgis
174 a3ad611d Dimitris Aragiorgis
175 a3ad611d Dimitris Aragiorgis
def _AppendForceIf(container, condition):
176 a3ad611d Dimitris Aragiorgis
  """Appends a "force" parameter if a condition evaluates to truth.
177 a3ad611d Dimitris Aragiorgis

178 a3ad611d Dimitris Aragiorgis
  """
179 a3ad611d Dimitris Aragiorgis
  return _AppendIf(container, condition, (_QPARAM_FORCE, 1))
180 a3ad611d Dimitris Aragiorgis
181 a3ad611d Dimitris Aragiorgis
182 a3ad611d Dimitris Aragiorgis
def _SetItemIf(container, condition, item, value):
183 a3ad611d Dimitris Aragiorgis
  """Sets an item if a condition evaluates to truth.
184 a3ad611d Dimitris Aragiorgis

185 a3ad611d Dimitris Aragiorgis
  """
186 a3ad611d Dimitris Aragiorgis
  if condition:
187 a3ad611d Dimitris Aragiorgis
    container[item] = value
188 a3ad611d Dimitris Aragiorgis
189 a3ad611d Dimitris Aragiorgis
  return condition
190 e5e20779 Faidon Liambotis
191 e5e20779 Faidon Liambotis
192 e5e20779 Faidon Liambotis
def UsesRapiClient(fn):
193 e5e20779 Faidon Liambotis
  """Decorator for code using RAPI client to initialize pycURL.
194 e5e20779 Faidon Liambotis

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

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

241 e5e20779 Faidon Liambotis
  """
242 e5e20779 Faidon Liambotis
  if use_curl_cabundle and (cafile or capath):
243 e5e20779 Faidon Liambotis
    raise Error("Can not use default CA bundle when CA file or path is set")
244 e5e20779 Faidon Liambotis
245 e5e20779 Faidon Liambotis
  def _ConfigCurl(curl, logger):
246 e5e20779 Faidon Liambotis
    """Configures a cURL object
247 e5e20779 Faidon Liambotis

248 e5e20779 Faidon Liambotis
    @type curl: pycurl.Curl
249 e5e20779 Faidon Liambotis
    @param curl: cURL object
250 e5e20779 Faidon Liambotis

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

317 e5e20779 Faidon Liambotis
  """
318 e5e20779 Faidon Liambotis
  USER_AGENT = "Ganeti RAPI Client"
319 a3ad611d Dimitris Aragiorgis
  _json_encoder = simplejson.JSONEncoder(sort_keys=True)
320 e5e20779 Faidon Liambotis
321 e5e20779 Faidon Liambotis
  def __init__(self, host, port=GANETI_RAPI_PORT,
322 a3ad611d Dimitris Aragiorgis
               username=None, password=None, logger=logging,
323 e5e20779 Faidon Liambotis
               curl_config_fn=None, curl_factory=None):
324 e5e20779 Faidon Liambotis
    """Initializes this class.
325 e5e20779 Faidon Liambotis

326 e5e20779 Faidon Liambotis
    @type host: string
327 e5e20779 Faidon Liambotis
    @param host: the ganeti cluster master to interact with
328 e5e20779 Faidon Liambotis
    @type port: int
329 e5e20779 Faidon Liambotis
    @param port: the port on which the RAPI is running (default is 5080)
330 e5e20779 Faidon Liambotis
    @type username: string
331 e5e20779 Faidon Liambotis
    @param username: the username to connect with
332 e5e20779 Faidon Liambotis
    @type password: string
333 e5e20779 Faidon Liambotis
    @param password: the password to connect with
334 e5e20779 Faidon Liambotis
    @type curl_config_fn: callable
335 e5e20779 Faidon Liambotis
    @param curl_config_fn: Function to configure C{pycurl.Curl} object
336 e5e20779 Faidon Liambotis
    @param logger: Logging object
337 e5e20779 Faidon Liambotis

338 e5e20779 Faidon Liambotis
    """
339 e5e20779 Faidon Liambotis
    self._username = username
340 e5e20779 Faidon Liambotis
    self._password = password
341 e5e20779 Faidon Liambotis
    self._logger = logger
342 e5e20779 Faidon Liambotis
    self._curl_config_fn = curl_config_fn
343 e5e20779 Faidon Liambotis
    self._curl_factory = curl_factory
344 e5e20779 Faidon Liambotis
345 e5e20779 Faidon Liambotis
    try:
346 e5e20779 Faidon Liambotis
      socket.inet_pton(socket.AF_INET6, host)
347 e5e20779 Faidon Liambotis
      address = "[%s]:%s" % (host, port)
348 e5e20779 Faidon Liambotis
    except socket.error:
349 e5e20779 Faidon Liambotis
      address = "%s:%s" % (host, port)
350 e5e20779 Faidon Liambotis
351 e5e20779 Faidon Liambotis
    self._base_url = "https://%s" % address
352 e5e20779 Faidon Liambotis
353 e5e20779 Faidon Liambotis
    if username is not None:
354 e5e20779 Faidon Liambotis
      if password is None:
355 e5e20779 Faidon Liambotis
        raise Error("Password not specified")
356 e5e20779 Faidon Liambotis
    elif password:
357 e5e20779 Faidon Liambotis
      raise Error("Specified password without username")
358 e5e20779 Faidon Liambotis
359 e5e20779 Faidon Liambotis
  def _CreateCurl(self):
360 e5e20779 Faidon Liambotis
    """Creates a cURL object.
361 e5e20779 Faidon Liambotis

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

401 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
402 e5e20779 Faidon Liambotis
    @param query: Query arguments
403 e5e20779 Faidon Liambotis
    @rtype: list
404 e5e20779 Faidon Liambotis
    @return: Query list with encoded values
405 e5e20779 Faidon Liambotis

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

428 e5e20779 Faidon Liambotis
    This constructs a full URL, encodes and decodes HTTP bodies, and
429 e5e20779 Faidon Liambotis
    handles invalid responses in a pythonic way.
430 e5e20779 Faidon Liambotis

431 e5e20779 Faidon Liambotis
    @type method: string
432 e5e20779 Faidon Liambotis
    @param method: HTTP method to use
433 e5e20779 Faidon Liambotis
    @type path: string
434 e5e20779 Faidon Liambotis
    @param path: HTTP URL path
435 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
436 e5e20779 Faidon Liambotis
    @param query: query arguments to pass to urllib.urlencode
437 e5e20779 Faidon Liambotis
    @type content: str or None
438 e5e20779 Faidon Liambotis
    @param content: HTTP body content
439 e5e20779 Faidon Liambotis

440 e5e20779 Faidon Liambotis
    @rtype: str
441 e5e20779 Faidon Liambotis
    @return: JSON-Decoded response
442 e5e20779 Faidon Liambotis

443 e5e20779 Faidon Liambotis
    @raises CertificateError: If an invalid SSL certificate is found
444 e5e20779 Faidon Liambotis
    @raises GanetiApiError: If an invalid response is returned
445 e5e20779 Faidon Liambotis

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

517 e5e20779 Faidon Liambotis
    @rtype: int
518 e5e20779 Faidon Liambotis
    @return: Ganeti Remote API version
519 e5e20779 Faidon Liambotis

520 e5e20779 Faidon Liambotis
    """
521 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/version", None, None)
522 e5e20779 Faidon Liambotis
523 e5e20779 Faidon Liambotis
  def GetFeatures(self):
524 e5e20779 Faidon Liambotis
    """Gets the list of optional features supported by RAPI server.
525 e5e20779 Faidon Liambotis

526 e5e20779 Faidon Liambotis
    @rtype: list
527 e5e20779 Faidon Liambotis
    @return: List of optional features
528 e5e20779 Faidon Liambotis

529 e5e20779 Faidon Liambotis
    """
530 e5e20779 Faidon Liambotis
    try:
531 e5e20779 Faidon Liambotis
      return self._SendRequest(HTTP_GET, "/%s/features" % GANETI_RAPI_VERSION,
532 e5e20779 Faidon Liambotis
                               None, None)
533 e5e20779 Faidon Liambotis
    except GanetiApiError, err:
534 e5e20779 Faidon Liambotis
      # Older RAPI servers don't support this resource
535 e5e20779 Faidon Liambotis
      if err.code == HTTP_NOT_FOUND:
536 e5e20779 Faidon Liambotis
        return []
537 e5e20779 Faidon Liambotis
538 e5e20779 Faidon Liambotis
      raise
539 e5e20779 Faidon Liambotis
540 e5e20779 Faidon Liambotis
  def GetOperatingSystems(self):
541 e5e20779 Faidon Liambotis
    """Gets the Operating Systems running in the Ganeti cluster.
542 e5e20779 Faidon Liambotis

543 e5e20779 Faidon Liambotis
    @rtype: list of str
544 e5e20779 Faidon Liambotis
    @return: operating systems
545 e5e20779 Faidon Liambotis

546 e5e20779 Faidon Liambotis
    """
547 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/os" % GANETI_RAPI_VERSION,
548 e5e20779 Faidon Liambotis
                             None, None)
549 e5e20779 Faidon Liambotis
550 e5e20779 Faidon Liambotis
  def GetInfo(self):
551 e5e20779 Faidon Liambotis
    """Gets info about the cluster.
552 e5e20779 Faidon Liambotis

553 e5e20779 Faidon Liambotis
    @rtype: dict
554 e5e20779 Faidon Liambotis
    @return: information about the cluster
555 e5e20779 Faidon Liambotis

556 e5e20779 Faidon Liambotis
    """
557 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/info" % GANETI_RAPI_VERSION,
558 e5e20779 Faidon Liambotis
                             None, None)
559 e5e20779 Faidon Liambotis
560 067dda99 Vangelis Koukis
  def RedistributeConfig(self):
561 067dda99 Vangelis Koukis
    """Tells the cluster to redistribute its configuration files.
562 067dda99 Vangelis Koukis

563 a3ad611d Dimitris Aragiorgis
    @rtype: string
564 067dda99 Vangelis Koukis
    @return: job id
565 067dda99 Vangelis Koukis

566 067dda99 Vangelis Koukis
    """
567 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
568 067dda99 Vangelis Koukis
                             "/%s/redistribute-config" % GANETI_RAPI_VERSION,
569 067dda99 Vangelis Koukis
                             None, None)
570 067dda99 Vangelis Koukis
571 067dda99 Vangelis Koukis
  def ModifyCluster(self, **kwargs):
572 067dda99 Vangelis Koukis
    """Modifies cluster parameters.
573 067dda99 Vangelis Koukis

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

576 a3ad611d Dimitris Aragiorgis
    @rtype: string
577 067dda99 Vangelis Koukis
    @return: job id
578 067dda99 Vangelis Koukis

579 067dda99 Vangelis Koukis
    """
580 067dda99 Vangelis Koukis
    body = kwargs
581 067dda99 Vangelis Koukis
582 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
583 067dda99 Vangelis Koukis
                             "/%s/modify" % GANETI_RAPI_VERSION, None, body)
584 067dda99 Vangelis Koukis
585 e5e20779 Faidon Liambotis
  def GetClusterTags(self):
586 e5e20779 Faidon Liambotis
    """Gets the cluster tags.
587 e5e20779 Faidon Liambotis

588 e5e20779 Faidon Liambotis
    @rtype: list of str
589 e5e20779 Faidon Liambotis
    @return: cluster tags
590 e5e20779 Faidon Liambotis

591 e5e20779 Faidon Liambotis
    """
592 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET, "/%s/tags" % GANETI_RAPI_VERSION,
593 e5e20779 Faidon Liambotis
                             None, None)
594 e5e20779 Faidon Liambotis
595 e5e20779 Faidon Liambotis
  def AddClusterTags(self, tags, dry_run=False):
596 e5e20779 Faidon Liambotis
    """Adds tags to the cluster.
597 e5e20779 Faidon Liambotis

598 e5e20779 Faidon Liambotis
    @type tags: list of str
599 e5e20779 Faidon Liambotis
    @param tags: tags to add to the cluster
600 e5e20779 Faidon Liambotis
    @type dry_run: bool
601 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
602 e5e20779 Faidon Liambotis

603 a3ad611d Dimitris Aragiorgis
    @rtype: string
604 e5e20779 Faidon Liambotis
    @return: job id
605 e5e20779 Faidon Liambotis

606 e5e20779 Faidon Liambotis
    """
607 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
608 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
609 e5e20779 Faidon Liambotis
610 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT, "/%s/tags" % GANETI_RAPI_VERSION,
611 e5e20779 Faidon Liambotis
                             query, None)
612 e5e20779 Faidon Liambotis
613 e5e20779 Faidon Liambotis
  def DeleteClusterTags(self, tags, dry_run=False):
614 e5e20779 Faidon Liambotis
    """Deletes tags from the cluster.
615 e5e20779 Faidon Liambotis

616 e5e20779 Faidon Liambotis
    @type tags: list of str
617 e5e20779 Faidon Liambotis
    @param tags: tags to delete
618 e5e20779 Faidon Liambotis
    @type dry_run: bool
619 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
620 a3ad611d Dimitris Aragiorgis
    @rtype: string
621 a3ad611d Dimitris Aragiorgis
    @return: job id
622 e5e20779 Faidon Liambotis

623 e5e20779 Faidon Liambotis
    """
624 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
625 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
626 e5e20779 Faidon Liambotis
627 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE, "/%s/tags" % GANETI_RAPI_VERSION,
628 e5e20779 Faidon Liambotis
                             query, None)
629 e5e20779 Faidon Liambotis
630 e5e20779 Faidon Liambotis
  def GetInstances(self, bulk=False):
631 e5e20779 Faidon Liambotis
    """Gets information about instances on the cluster.
632 e5e20779 Faidon Liambotis

633 e5e20779 Faidon Liambotis
    @type bulk: bool
634 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
635 e5e20779 Faidon Liambotis

636 e5e20779 Faidon Liambotis
    @rtype: list of dict or list of str
637 e5e20779 Faidon Liambotis
    @return: if bulk is True, info about the instances, else a list of instances
638 e5e20779 Faidon Liambotis

639 e5e20779 Faidon Liambotis
    """
640 e5e20779 Faidon Liambotis
    query = []
641 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
642 e5e20779 Faidon Liambotis
643 e5e20779 Faidon Liambotis
    instances = self._SendRequest(HTTP_GET,
644 e5e20779 Faidon Liambotis
                                  "/%s/instances" % GANETI_RAPI_VERSION,
645 e5e20779 Faidon Liambotis
                                  query, None)
646 e5e20779 Faidon Liambotis
    if bulk:
647 e5e20779 Faidon Liambotis
      return instances
648 e5e20779 Faidon Liambotis
    else:
649 e5e20779 Faidon Liambotis
      return [i["id"] for i in instances]
650 e5e20779 Faidon Liambotis
651 e5e20779 Faidon Liambotis
  def GetInstance(self, instance):
652 e5e20779 Faidon Liambotis
    """Gets information about an instance.
653 e5e20779 Faidon Liambotis

654 e5e20779 Faidon Liambotis
    @type instance: str
655 e5e20779 Faidon Liambotis
    @param instance: instance whose info to return
656 e5e20779 Faidon Liambotis

657 e5e20779 Faidon Liambotis
    @rtype: dict
658 e5e20779 Faidon Liambotis
    @return: info about the instance
659 e5e20779 Faidon Liambotis

660 e5e20779 Faidon Liambotis
    """
661 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
662 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
663 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
664 e5e20779 Faidon Liambotis
665 e5e20779 Faidon Liambotis
  def GetInstanceInfo(self, instance, static=None):
666 e5e20779 Faidon Liambotis
    """Gets information about an instance.
667 e5e20779 Faidon Liambotis

668 e5e20779 Faidon Liambotis
    @type instance: string
669 e5e20779 Faidon Liambotis
    @param instance: Instance name
670 e5e20779 Faidon Liambotis
    @rtype: string
671 e5e20779 Faidon Liambotis
    @return: Job ID
672 e5e20779 Faidon Liambotis

673 e5e20779 Faidon Liambotis
    """
674 e5e20779 Faidon Liambotis
    if static is not None:
675 e5e20779 Faidon Liambotis
      query = [("static", static)]
676 e5e20779 Faidon Liambotis
    else:
677 e5e20779 Faidon Liambotis
      query = None
678 e5e20779 Faidon Liambotis
679 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
680 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/info" %
681 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
682 e5e20779 Faidon Liambotis
683 e5e20779 Faidon Liambotis
  def CreateInstance(self, mode, name, disk_template, disks, nics,
684 e5e20779 Faidon Liambotis
                     **kwargs):
685 e5e20779 Faidon Liambotis
    """Creates a new instance.
686 e5e20779 Faidon Liambotis

687 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
688 e5e20779 Faidon Liambotis

689 e5e20779 Faidon Liambotis
    @type mode: string
690 e5e20779 Faidon Liambotis
    @param mode: Instance creation mode
691 e5e20779 Faidon Liambotis
    @type name: string
692 e5e20779 Faidon Liambotis
    @param name: Hostname of the instance to create
693 e5e20779 Faidon Liambotis
    @type disk_template: string
694 e5e20779 Faidon Liambotis
    @param disk_template: Disk template for instance (e.g. plain, diskless,
695 e5e20779 Faidon Liambotis
                          file, or drbd)
696 e5e20779 Faidon Liambotis
    @type disks: list of dicts
697 e5e20779 Faidon Liambotis
    @param disks: List of disk definitions
698 e5e20779 Faidon Liambotis
    @type nics: list of dicts
699 e5e20779 Faidon Liambotis
    @param nics: List of NIC definitions
700 e5e20779 Faidon Liambotis
    @type dry_run: bool
701 e5e20779 Faidon Liambotis
    @keyword dry_run: whether to perform a dry run
702 e5e20779 Faidon Liambotis

703 a3ad611d Dimitris Aragiorgis
    @rtype: string
704 e5e20779 Faidon Liambotis
    @return: job id
705 e5e20779 Faidon Liambotis

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

739 e5e20779 Faidon Liambotis
    @type instance: str
740 e5e20779 Faidon Liambotis
    @param instance: the instance to delete
741 e5e20779 Faidon Liambotis

742 a3ad611d Dimitris Aragiorgis
    @rtype: string
743 e5e20779 Faidon Liambotis
    @return: job id
744 e5e20779 Faidon Liambotis

745 e5e20779 Faidon Liambotis
    """
746 e5e20779 Faidon Liambotis
    query = []
747 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
748 e5e20779 Faidon Liambotis
749 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
750 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s" %
751 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
752 e5e20779 Faidon Liambotis
753 e5e20779 Faidon Liambotis
  def ModifyInstance(self, instance, **kwargs):
754 e5e20779 Faidon Liambotis
    """Modifies an instance.
755 e5e20779 Faidon Liambotis

756 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
757 e5e20779 Faidon Liambotis

758 e5e20779 Faidon Liambotis
    @type instance: string
759 e5e20779 Faidon Liambotis
    @param instance: Instance name
760 a3ad611d Dimitris Aragiorgis
    @rtype: string
761 e5e20779 Faidon Liambotis
    @return: job id
762 e5e20779 Faidon Liambotis

763 e5e20779 Faidon Liambotis
    """
764 e5e20779 Faidon Liambotis
    body = kwargs
765 e5e20779 Faidon Liambotis
766 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
767 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/modify" %
768 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
769 e5e20779 Faidon Liambotis
770 067dda99 Vangelis Koukis
  def ActivateInstanceDisks(self, instance, ignore_size=None):
771 067dda99 Vangelis Koukis
    """Activates an instance's disks.
772 067dda99 Vangelis Koukis

773 067dda99 Vangelis Koukis
    @type instance: string
774 067dda99 Vangelis Koukis
    @param instance: Instance name
775 067dda99 Vangelis Koukis
    @type ignore_size: bool
776 067dda99 Vangelis Koukis
    @param ignore_size: Whether to ignore recorded size
777 a3ad611d Dimitris Aragiorgis
    @rtype: string
778 067dda99 Vangelis Koukis
    @return: job id
779 067dda99 Vangelis Koukis

780 067dda99 Vangelis Koukis
    """
781 067dda99 Vangelis Koukis
    query = []
782 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, ignore_size, ("ignore_size", 1))
783 067dda99 Vangelis Koukis
784 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
785 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/activate-disks" %
786 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), query, None)
787 067dda99 Vangelis Koukis
788 067dda99 Vangelis Koukis
  def DeactivateInstanceDisks(self, instance):
789 067dda99 Vangelis Koukis
    """Deactivates an instance's disks.
790 067dda99 Vangelis Koukis

791 067dda99 Vangelis Koukis
    @type instance: string
792 067dda99 Vangelis Koukis
    @param instance: Instance name
793 a3ad611d Dimitris Aragiorgis
    @rtype: string
794 067dda99 Vangelis Koukis
    @return: job id
795 067dda99 Vangelis Koukis

796 067dda99 Vangelis Koukis
    """
797 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
798 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/deactivate-disks" %
799 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
800 067dda99 Vangelis Koukis
801 a3ad611d Dimitris Aragiorgis
  def RecreateInstanceDisks(self, instance, disks=None, nodes=None):
802 a3ad611d Dimitris Aragiorgis
    """Recreate an instance's disks.
803 a3ad611d Dimitris Aragiorgis

804 a3ad611d Dimitris Aragiorgis
    @type instance: string
805 a3ad611d Dimitris Aragiorgis
    @param instance: Instance name
806 a3ad611d Dimitris Aragiorgis
    @type disks: list of int
807 a3ad611d Dimitris Aragiorgis
    @param disks: List of disk indexes
808 a3ad611d Dimitris Aragiorgis
    @type nodes: list of string
809 a3ad611d Dimitris Aragiorgis
    @param nodes: New instance nodes, if relocation is desired
810 a3ad611d Dimitris Aragiorgis
    @rtype: string
811 a3ad611d Dimitris Aragiorgis
    @return: job id
812 a3ad611d Dimitris Aragiorgis

813 a3ad611d Dimitris Aragiorgis
    """
814 a3ad611d Dimitris Aragiorgis
    body = {}
815 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, disks is not None, "disks", disks)
816 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, nodes is not None, "nodes", nodes)
817 a3ad611d Dimitris Aragiorgis
818 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
819 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/recreate-disks" %
820 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), None, body)
821 a3ad611d Dimitris Aragiorgis
822 067dda99 Vangelis Koukis
  def GrowInstanceDisk(self, instance, disk, amount, wait_for_sync=None):
823 067dda99 Vangelis Koukis
    """Grows a disk of an instance.
824 067dda99 Vangelis Koukis

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

827 067dda99 Vangelis Koukis
    @type instance: string
828 067dda99 Vangelis Koukis
    @param instance: Instance name
829 067dda99 Vangelis Koukis
    @type disk: integer
830 067dda99 Vangelis Koukis
    @param disk: Disk index
831 067dda99 Vangelis Koukis
    @type amount: integer
832 067dda99 Vangelis Koukis
    @param amount: Grow disk by this amount (MiB)
833 067dda99 Vangelis Koukis
    @type wait_for_sync: bool
834 067dda99 Vangelis Koukis
    @param wait_for_sync: Wait for disk to synchronize
835 a3ad611d Dimitris Aragiorgis
    @rtype: string
836 067dda99 Vangelis Koukis
    @return: job id
837 067dda99 Vangelis Koukis

838 067dda99 Vangelis Koukis
    """
839 067dda99 Vangelis Koukis
    body = {
840 067dda99 Vangelis Koukis
      "amount": amount,
841 067dda99 Vangelis Koukis
      }
842 067dda99 Vangelis Koukis
843 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, wait_for_sync is not None, "wait_for_sync", wait_for_sync)
844 067dda99 Vangelis Koukis
845 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST,
846 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/disk/%s/grow" %
847 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance, disk)),
848 067dda99 Vangelis Koukis
                             None, body)
849 067dda99 Vangelis Koukis
850 e5e20779 Faidon Liambotis
  def GetInstanceTags(self, instance):
851 e5e20779 Faidon Liambotis
    """Gets tags for an instance.
852 e5e20779 Faidon Liambotis

853 e5e20779 Faidon Liambotis
    @type instance: str
854 e5e20779 Faidon Liambotis
    @param instance: instance whose tags to return
855 e5e20779 Faidon Liambotis

856 e5e20779 Faidon Liambotis
    @rtype: list of str
857 e5e20779 Faidon Liambotis
    @return: tags for the instance
858 e5e20779 Faidon Liambotis

859 e5e20779 Faidon Liambotis
    """
860 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
861 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
862 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, None)
863 e5e20779 Faidon Liambotis
864 e5e20779 Faidon Liambotis
  def AddInstanceTags(self, instance, tags, dry_run=False):
865 e5e20779 Faidon Liambotis
    """Adds tags to an instance.
866 e5e20779 Faidon Liambotis

867 e5e20779 Faidon Liambotis
    @type instance: str
868 e5e20779 Faidon Liambotis
    @param instance: instance to add tags to
869 e5e20779 Faidon Liambotis
    @type tags: list of str
870 e5e20779 Faidon Liambotis
    @param tags: tags to add to the instance
871 e5e20779 Faidon Liambotis
    @type dry_run: bool
872 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
873 e5e20779 Faidon Liambotis

874 a3ad611d Dimitris Aragiorgis
    @rtype: string
875 e5e20779 Faidon Liambotis
    @return: job id
876 e5e20779 Faidon Liambotis

877 e5e20779 Faidon Liambotis
    """
878 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
879 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
880 e5e20779 Faidon Liambotis
881 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
882 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
883 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
884 e5e20779 Faidon Liambotis
885 e5e20779 Faidon Liambotis
  def DeleteInstanceTags(self, instance, tags, dry_run=False):
886 e5e20779 Faidon Liambotis
    """Deletes tags from an instance.
887 e5e20779 Faidon Liambotis

888 e5e20779 Faidon Liambotis
    @type instance: str
889 e5e20779 Faidon Liambotis
    @param instance: instance to delete tags from
890 e5e20779 Faidon Liambotis
    @type tags: list of str
891 e5e20779 Faidon Liambotis
    @param tags: tags to delete
892 e5e20779 Faidon Liambotis
    @type dry_run: bool
893 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
894 a3ad611d Dimitris Aragiorgis
    @rtype: string
895 a3ad611d Dimitris Aragiorgis
    @return: job id
896 e5e20779 Faidon Liambotis

897 e5e20779 Faidon Liambotis
    """
898 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
899 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
900 e5e20779 Faidon Liambotis
901 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
902 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/tags" %
903 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
904 e5e20779 Faidon Liambotis
905 e5e20779 Faidon Liambotis
  def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
906 e5e20779 Faidon Liambotis
                     dry_run=False):
907 e5e20779 Faidon Liambotis
    """Reboots an instance.
908 e5e20779 Faidon Liambotis

909 e5e20779 Faidon Liambotis
    @type instance: str
910 e5e20779 Faidon Liambotis
    @param instance: instance to rebot
911 e5e20779 Faidon Liambotis
    @type reboot_type: str
912 e5e20779 Faidon Liambotis
    @param reboot_type: one of: hard, soft, full
913 e5e20779 Faidon Liambotis
    @type ignore_secondaries: bool
914 e5e20779 Faidon Liambotis
    @param ignore_secondaries: if True, ignores errors for the secondary node
915 e5e20779 Faidon Liambotis
        while re-assembling disks (in hard-reboot mode only)
916 e5e20779 Faidon Liambotis
    @type dry_run: bool
917 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
918 a3ad611d Dimitris Aragiorgis
    @rtype: string
919 a3ad611d Dimitris Aragiorgis
    @return: job id
920 e5e20779 Faidon Liambotis

921 e5e20779 Faidon Liambotis
    """
922 e5e20779 Faidon Liambotis
    query = []
923 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
924 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, reboot_type, ("type", reboot_type))
925 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, ignore_secondaries is not None,
926 a3ad611d Dimitris Aragiorgis
              ("ignore_secondaries", ignore_secondaries))
927 e5e20779 Faidon Liambotis
928 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
929 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/reboot" %
930 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
931 e5e20779 Faidon Liambotis
932 a3ad611d Dimitris Aragiorgis
  def ShutdownInstance(self, instance, dry_run=False, no_remember=False):
933 e5e20779 Faidon Liambotis
    """Shuts down an instance.
934 e5e20779 Faidon Liambotis

935 e5e20779 Faidon Liambotis
    @type instance: str
936 e5e20779 Faidon Liambotis
    @param instance: the instance to shut down
937 e5e20779 Faidon Liambotis
    @type dry_run: bool
938 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
939 a3ad611d Dimitris Aragiorgis
    @type no_remember: bool
940 a3ad611d Dimitris Aragiorgis
    @param no_remember: if true, will not record the state change
941 a3ad611d Dimitris Aragiorgis
    @rtype: string
942 a3ad611d Dimitris Aragiorgis
    @return: job id
943 e5e20779 Faidon Liambotis

944 e5e20779 Faidon Liambotis
    """
945 e5e20779 Faidon Liambotis
    query = []
946 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
947 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, no_remember, ("no-remember", 1))
948 e5e20779 Faidon Liambotis
949 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
950 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/shutdown" %
951 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
952 e5e20779 Faidon Liambotis
953 a3ad611d Dimitris Aragiorgis
  def StartupInstance(self, instance, dry_run=False, no_remember=False):
954 e5e20779 Faidon Liambotis
    """Starts up an instance.
955 e5e20779 Faidon Liambotis

956 e5e20779 Faidon Liambotis
    @type instance: str
957 e5e20779 Faidon Liambotis
    @param instance: the instance to start up
958 e5e20779 Faidon Liambotis
    @type dry_run: bool
959 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
960 a3ad611d Dimitris Aragiorgis
    @type no_remember: bool
961 a3ad611d Dimitris Aragiorgis
    @param no_remember: if true, will not record the state change
962 a3ad611d Dimitris Aragiorgis
    @rtype: string
963 a3ad611d Dimitris Aragiorgis
    @return: job id
964 e5e20779 Faidon Liambotis

965 e5e20779 Faidon Liambotis
    """
966 e5e20779 Faidon Liambotis
    query = []
967 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
968 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, no_remember, ("no-remember", 1))
969 e5e20779 Faidon Liambotis
970 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
971 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/startup" %
972 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
973 e5e20779 Faidon Liambotis
974 067dda99 Vangelis Koukis
  def ReinstallInstance(self, instance, os=None, no_startup=False,
975 067dda99 Vangelis Koukis
                        osparams=None):
976 e5e20779 Faidon Liambotis
    """Reinstalls an instance.
977 e5e20779 Faidon Liambotis

978 e5e20779 Faidon Liambotis
    @type instance: str
979 e5e20779 Faidon Liambotis
    @param instance: The instance to reinstall
980 e5e20779 Faidon Liambotis
    @type os: str or None
981 e5e20779 Faidon Liambotis
    @param os: The operating system to reinstall. If None, the instance's
982 e5e20779 Faidon Liambotis
        current operating system will be installed again
983 e5e20779 Faidon Liambotis
    @type no_startup: bool
984 e5e20779 Faidon Liambotis
    @param no_startup: Whether to start the instance automatically
985 a3ad611d Dimitris Aragiorgis
    @rtype: string
986 a3ad611d Dimitris Aragiorgis
    @return: job id
987 e5e20779 Faidon Liambotis

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

1016 e5e20779 Faidon Liambotis
    @type instance: str
1017 e5e20779 Faidon Liambotis
    @param instance: instance whose disks to replace
1018 e5e20779 Faidon Liambotis
    @type disks: list of ints
1019 e5e20779 Faidon Liambotis
    @param disks: Indexes of disks to replace
1020 e5e20779 Faidon Liambotis
    @type mode: str
1021 e5e20779 Faidon Liambotis
    @param mode: replacement mode to use (defaults to replace_auto)
1022 e5e20779 Faidon Liambotis
    @type remote_node: str or None
1023 e5e20779 Faidon Liambotis
    @param remote_node: new secondary node to use (for use with
1024 e5e20779 Faidon Liambotis
        replace_new_secondary mode)
1025 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1026 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator plugin to use (for use with
1027 e5e20779 Faidon Liambotis
                       replace_auto mode)
1028 e5e20779 Faidon Liambotis

1029 a3ad611d Dimitris Aragiorgis
    @rtype: string
1030 e5e20779 Faidon Liambotis
    @return: job id
1031 e5e20779 Faidon Liambotis

1032 e5e20779 Faidon Liambotis
    """
1033 e5e20779 Faidon Liambotis
    query = [
1034 e5e20779 Faidon Liambotis
      ("mode", mode),
1035 e5e20779 Faidon Liambotis
      ]
1036 e5e20779 Faidon Liambotis
1037 a3ad611d Dimitris Aragiorgis
    # TODO: Convert to body parameters
1038 e5e20779 Faidon Liambotis
1039 a3ad611d Dimitris Aragiorgis
    if disks is not None:
1040 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, True,
1041 a3ad611d Dimitris Aragiorgis
                ("disks", ",".join(str(idx) for idx in disks)))
1042 e5e20779 Faidon Liambotis
1043 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, remote_node is not None, ("remote_node", remote_node))
1044 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, iallocator is not None, ("iallocator", iallocator))
1045 e5e20779 Faidon Liambotis
1046 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_POST,
1047 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/replace-disks" %
1048 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1049 e5e20779 Faidon Liambotis
1050 e5e20779 Faidon Liambotis
  def PrepareExport(self, instance, mode):
1051 e5e20779 Faidon Liambotis
    """Prepares an instance for an export.
1052 e5e20779 Faidon Liambotis

1053 e5e20779 Faidon Liambotis
    @type instance: string
1054 e5e20779 Faidon Liambotis
    @param instance: Instance name
1055 e5e20779 Faidon Liambotis
    @type mode: string
1056 e5e20779 Faidon Liambotis
    @param mode: Export mode
1057 e5e20779 Faidon Liambotis
    @rtype: string
1058 e5e20779 Faidon Liambotis
    @return: Job ID
1059 e5e20779 Faidon Liambotis

1060 e5e20779 Faidon Liambotis
    """
1061 e5e20779 Faidon Liambotis
    query = [("mode", mode)]
1062 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1063 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/prepare-export" %
1064 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), query, None)
1065 e5e20779 Faidon Liambotis
1066 e5e20779 Faidon Liambotis
  def ExportInstance(self, instance, mode, destination, shutdown=None,
1067 e5e20779 Faidon Liambotis
                     remove_instance=None,
1068 e5e20779 Faidon Liambotis
                     x509_key_name=None, destination_x509_ca=None):
1069 e5e20779 Faidon Liambotis
    """Exports an instance.
1070 e5e20779 Faidon Liambotis

1071 e5e20779 Faidon Liambotis
    @type instance: string
1072 e5e20779 Faidon Liambotis
    @param instance: Instance name
1073 e5e20779 Faidon Liambotis
    @type mode: string
1074 e5e20779 Faidon Liambotis
    @param mode: Export mode
1075 e5e20779 Faidon Liambotis
    @rtype: string
1076 e5e20779 Faidon Liambotis
    @return: Job ID
1077 e5e20779 Faidon Liambotis

1078 e5e20779 Faidon Liambotis
    """
1079 e5e20779 Faidon Liambotis
    body = {
1080 e5e20779 Faidon Liambotis
      "destination": destination,
1081 e5e20779 Faidon Liambotis
      "mode": mode,
1082 e5e20779 Faidon Liambotis
      }
1083 e5e20779 Faidon Liambotis
1084 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, shutdown is not None, "shutdown", shutdown)
1085 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, remove_instance is not None,
1086 a3ad611d Dimitris Aragiorgis
               "remove_instance", remove_instance)
1087 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, x509_key_name is not None, "x509_key_name", x509_key_name)
1088 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, destination_x509_ca is not None,
1089 a3ad611d Dimitris Aragiorgis
               "destination_x509_ca", destination_x509_ca)
1090 e5e20779 Faidon Liambotis
1091 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1092 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/export" %
1093 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1094 e5e20779 Faidon Liambotis
1095 e5e20779 Faidon Liambotis
  def MigrateInstance(self, instance, mode=None, cleanup=None):
1096 e5e20779 Faidon Liambotis
    """Migrates an instance.
1097 e5e20779 Faidon Liambotis

1098 e5e20779 Faidon Liambotis
    @type instance: string
1099 e5e20779 Faidon Liambotis
    @param instance: Instance name
1100 e5e20779 Faidon Liambotis
    @type mode: string
1101 e5e20779 Faidon Liambotis
    @param mode: Migration mode
1102 e5e20779 Faidon Liambotis
    @type cleanup: bool
1103 e5e20779 Faidon Liambotis
    @param cleanup: Whether to clean up a previously failed migration
1104 a3ad611d Dimitris Aragiorgis
    @rtype: string
1105 a3ad611d Dimitris Aragiorgis
    @return: job id
1106 e5e20779 Faidon Liambotis

1107 e5e20779 Faidon Liambotis
    """
1108 e5e20779 Faidon Liambotis
    body = {}
1109 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, mode is not None, "mode", mode)
1110 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, cleanup is not None, "cleanup", cleanup)
1111 e5e20779 Faidon Liambotis
1112 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1113 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/migrate" %
1114 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1115 e5e20779 Faidon Liambotis
1116 a3ad611d Dimitris Aragiorgis
  def FailoverInstance(self, instance, iallocator=None,
1117 a3ad611d Dimitris Aragiorgis
                       ignore_consistency=None, target_node=None):
1118 a3ad611d Dimitris Aragiorgis
    """Does a failover of an instance.
1119 a3ad611d Dimitris Aragiorgis

1120 a3ad611d Dimitris Aragiorgis
    @type instance: string
1121 a3ad611d Dimitris Aragiorgis
    @param instance: Instance name
1122 a3ad611d Dimitris Aragiorgis
    @type iallocator: string
1123 a3ad611d Dimitris Aragiorgis
    @param iallocator: Iallocator for deciding the target node for
1124 a3ad611d Dimitris Aragiorgis
      shared-storage instances
1125 a3ad611d Dimitris Aragiorgis
    @type ignore_consistency: bool
1126 a3ad611d Dimitris Aragiorgis
    @param ignore_consistency: Whether to ignore disk consistency
1127 a3ad611d Dimitris Aragiorgis
    @type target_node: string
1128 a3ad611d Dimitris Aragiorgis
    @param target_node: Target node for shared-storage instances
1129 a3ad611d Dimitris Aragiorgis
    @rtype: string
1130 a3ad611d Dimitris Aragiorgis
    @return: job id
1131 a3ad611d Dimitris Aragiorgis

1132 a3ad611d Dimitris Aragiorgis
    """
1133 a3ad611d Dimitris Aragiorgis
    body = {}
1134 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, iallocator is not None, "iallocator", iallocator)
1135 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, ignore_consistency is not None,
1136 a3ad611d Dimitris Aragiorgis
               "ignore_consistency", ignore_consistency)
1137 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, target_node is not None, "target_node", target_node)
1138 e5e20779 Faidon Liambotis
1139 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1140 a3ad611d Dimitris Aragiorgis
                             ("/%s/instances/%s/failover" %
1141 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1142 e5e20779 Faidon Liambotis
1143 e5e20779 Faidon Liambotis
  def RenameInstance(self, instance, new_name, ip_check=None, name_check=None):
1144 e5e20779 Faidon Liambotis
    """Changes the name of an instance.
1145 e5e20779 Faidon Liambotis

1146 e5e20779 Faidon Liambotis
    @type instance: string
1147 e5e20779 Faidon Liambotis
    @param instance: Instance name
1148 e5e20779 Faidon Liambotis
    @type new_name: string
1149 e5e20779 Faidon Liambotis
    @param new_name: New instance name
1150 e5e20779 Faidon Liambotis
    @type ip_check: bool
1151 e5e20779 Faidon Liambotis
    @param ip_check: Whether to ensure instance's IP address is inactive
1152 e5e20779 Faidon Liambotis
    @type name_check: bool
1153 e5e20779 Faidon Liambotis
    @param name_check: Whether to ensure instance's name is resolvable
1154 a3ad611d Dimitris Aragiorgis
    @rtype: string
1155 a3ad611d Dimitris Aragiorgis
    @return: job id
1156 e5e20779 Faidon Liambotis

1157 e5e20779 Faidon Liambotis
    """
1158 e5e20779 Faidon Liambotis
    body = {
1159 e5e20779 Faidon Liambotis
      "new_name": new_name,
1160 e5e20779 Faidon Liambotis
      }
1161 e5e20779 Faidon Liambotis
1162 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, ip_check is not None, "ip_check", ip_check)
1163 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, name_check is not None, "name_check", name_check)
1164 e5e20779 Faidon Liambotis
1165 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1166 e5e20779 Faidon Liambotis
                             ("/%s/instances/%s/rename" %
1167 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, instance)), None, body)
1168 e5e20779 Faidon Liambotis
1169 067dda99 Vangelis Koukis
  def GetInstanceConsole(self, instance):
1170 067dda99 Vangelis Koukis
    """Request information for connecting to instance's console.
1171 067dda99 Vangelis Koukis

1172 067dda99 Vangelis Koukis
    @type instance: string
1173 067dda99 Vangelis Koukis
    @param instance: Instance name
1174 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1175 a3ad611d Dimitris Aragiorgis
    @return: dictionary containing information about instance's console
1176 067dda99 Vangelis Koukis

1177 067dda99 Vangelis Koukis
    """
1178 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1179 067dda99 Vangelis Koukis
                             ("/%s/instances/%s/console" %
1180 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, instance)), None, None)
1181 067dda99 Vangelis Koukis
1182 e5e20779 Faidon Liambotis
  def GetJobs(self):
1183 e5e20779 Faidon Liambotis
    """Gets all jobs for the cluster.
1184 e5e20779 Faidon Liambotis

1185 e5e20779 Faidon Liambotis
    @rtype: list of int
1186 e5e20779 Faidon Liambotis
    @return: job ids for the cluster
1187 e5e20779 Faidon Liambotis

1188 e5e20779 Faidon Liambotis
    """
1189 e5e20779 Faidon Liambotis
    return [int(j["id"])
1190 e5e20779 Faidon Liambotis
            for j in self._SendRequest(HTTP_GET,
1191 e5e20779 Faidon Liambotis
                                       "/%s/jobs" % GANETI_RAPI_VERSION,
1192 e5e20779 Faidon Liambotis
                                       None, None)]
1193 e5e20779 Faidon Liambotis
1194 e5e20779 Faidon Liambotis
  def GetJobStatus(self, job_id):
1195 e5e20779 Faidon Liambotis
    """Gets the status of a job.
1196 e5e20779 Faidon Liambotis

1197 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1198 e5e20779 Faidon Liambotis
    @param job_id: job id whose status to query
1199 e5e20779 Faidon Liambotis

1200 e5e20779 Faidon Liambotis
    @rtype: dict
1201 e5e20779 Faidon Liambotis
    @return: job status
1202 e5e20779 Faidon Liambotis

1203 e5e20779 Faidon Liambotis
    """
1204 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1205 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1206 e5e20779 Faidon Liambotis
                             None, None)
1207 e5e20779 Faidon Liambotis
1208 a3ad611d Dimitris Aragiorgis
  def WaitForJobCompletion(self, job_id, period=5, retries=-1):
1209 a3ad611d Dimitris Aragiorgis
    """Polls cluster for job status until completion.
1210 a3ad611d Dimitris Aragiorgis

1211 a3ad611d Dimitris Aragiorgis
    Completion is defined as any of the following states listed in
1212 a3ad611d Dimitris Aragiorgis
    L{JOB_STATUS_FINALIZED}.
1213 a3ad611d Dimitris Aragiorgis

1214 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1215 a3ad611d Dimitris Aragiorgis
    @param job_id: job id to watch
1216 a3ad611d Dimitris Aragiorgis
    @type period: int
1217 a3ad611d Dimitris Aragiorgis
    @param period: how often to poll for status (optional, default 5s)
1218 a3ad611d Dimitris Aragiorgis
    @type retries: int
1219 a3ad611d Dimitris Aragiorgis
    @param retries: how many time to poll before giving up
1220 a3ad611d Dimitris Aragiorgis
                    (optional, default -1 means unlimited)
1221 a3ad611d Dimitris Aragiorgis

1222 a3ad611d Dimitris Aragiorgis
    @rtype: bool
1223 a3ad611d Dimitris Aragiorgis
    @return: C{True} if job succeeded or C{False} if failed/status timeout
1224 a3ad611d Dimitris Aragiorgis
    @deprecated: It is recommended to use L{WaitForJobChange} wherever
1225 a3ad611d Dimitris Aragiorgis
      possible; L{WaitForJobChange} returns immediately after a job changed and
1226 a3ad611d Dimitris Aragiorgis
      does not use polling
1227 a3ad611d Dimitris Aragiorgis

1228 a3ad611d Dimitris Aragiorgis
    """
1229 a3ad611d Dimitris Aragiorgis
    while retries != 0:
1230 a3ad611d Dimitris Aragiorgis
      job_result = self.GetJobStatus(job_id)
1231 a3ad611d Dimitris Aragiorgis
1232 a3ad611d Dimitris Aragiorgis
      if job_result and job_result["status"] == JOB_STATUS_SUCCESS:
1233 a3ad611d Dimitris Aragiorgis
        return True
1234 a3ad611d Dimitris Aragiorgis
      elif not job_result or job_result["status"] in JOB_STATUS_FINALIZED:
1235 a3ad611d Dimitris Aragiorgis
        return False
1236 a3ad611d Dimitris Aragiorgis
1237 a3ad611d Dimitris Aragiorgis
      if period:
1238 a3ad611d Dimitris Aragiorgis
        time.sleep(period)
1239 a3ad611d Dimitris Aragiorgis
1240 a3ad611d Dimitris Aragiorgis
      if retries > 0:
1241 a3ad611d Dimitris Aragiorgis
        retries -= 1
1242 a3ad611d Dimitris Aragiorgis
1243 a3ad611d Dimitris Aragiorgis
    return False
1244 a3ad611d Dimitris Aragiorgis
1245 e5e20779 Faidon Liambotis
  def WaitForJobChange(self, job_id, fields, prev_job_info, prev_log_serial):
1246 e5e20779 Faidon Liambotis
    """Waits for job changes.
1247 e5e20779 Faidon Liambotis

1248 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1249 e5e20779 Faidon Liambotis
    @param job_id: Job ID for which to wait
1250 a3ad611d Dimitris Aragiorgis
    @return: C{None} if no changes have been detected and a dict with two keys,
1251 a3ad611d Dimitris Aragiorgis
      C{job_info} and C{log_entries} otherwise.
1252 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1253 e5e20779 Faidon Liambotis

1254 e5e20779 Faidon Liambotis
    """
1255 e5e20779 Faidon Liambotis
    body = {
1256 e5e20779 Faidon Liambotis
      "fields": fields,
1257 e5e20779 Faidon Liambotis
      "previous_job_info": prev_job_info,
1258 e5e20779 Faidon Liambotis
      "previous_log_serial": prev_log_serial,
1259 e5e20779 Faidon Liambotis
      }
1260 e5e20779 Faidon Liambotis
1261 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1262 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s/wait" % (GANETI_RAPI_VERSION, job_id),
1263 e5e20779 Faidon Liambotis
                             None, body)
1264 e5e20779 Faidon Liambotis
1265 e5e20779 Faidon Liambotis
  def CancelJob(self, job_id, dry_run=False):
1266 e5e20779 Faidon Liambotis
    """Cancels a job.
1267 e5e20779 Faidon Liambotis

1268 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1269 e5e20779 Faidon Liambotis
    @param job_id: id of the job to delete
1270 e5e20779 Faidon Liambotis
    @type dry_run: bool
1271 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1272 a3ad611d Dimitris Aragiorgis
    @rtype: tuple
1273 a3ad611d Dimitris Aragiorgis
    @return: tuple containing the result, and a message (bool, string)
1274 e5e20779 Faidon Liambotis

1275 e5e20779 Faidon Liambotis
    """
1276 e5e20779 Faidon Liambotis
    query = []
1277 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1278 e5e20779 Faidon Liambotis
1279 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1280 e5e20779 Faidon Liambotis
                             "/%s/jobs/%s" % (GANETI_RAPI_VERSION, job_id),
1281 e5e20779 Faidon Liambotis
                             query, None)
1282 e5e20779 Faidon Liambotis
1283 e5e20779 Faidon Liambotis
  def GetNodes(self, bulk=False):
1284 e5e20779 Faidon Liambotis
    """Gets all nodes in the cluster.
1285 e5e20779 Faidon Liambotis

1286 e5e20779 Faidon Liambotis
    @type bulk: bool
1287 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
1288 e5e20779 Faidon Liambotis

1289 e5e20779 Faidon Liambotis
    @rtype: list of dict or str
1290 e5e20779 Faidon Liambotis
    @return: if bulk is true, info about nodes in the cluster,
1291 e5e20779 Faidon Liambotis
        else list of nodes in the cluster
1292 e5e20779 Faidon Liambotis

1293 e5e20779 Faidon Liambotis
    """
1294 e5e20779 Faidon Liambotis
    query = []
1295 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1296 e5e20779 Faidon Liambotis
1297 e5e20779 Faidon Liambotis
    nodes = self._SendRequest(HTTP_GET, "/%s/nodes" % GANETI_RAPI_VERSION,
1298 e5e20779 Faidon Liambotis
                              query, None)
1299 e5e20779 Faidon Liambotis
    if bulk:
1300 e5e20779 Faidon Liambotis
      return nodes
1301 e5e20779 Faidon Liambotis
    else:
1302 e5e20779 Faidon Liambotis
      return [n["id"] for n in nodes]
1303 e5e20779 Faidon Liambotis
1304 e5e20779 Faidon Liambotis
  def GetNode(self, node):
1305 e5e20779 Faidon Liambotis
    """Gets information about a node.
1306 e5e20779 Faidon Liambotis

1307 e5e20779 Faidon Liambotis
    @type node: str
1308 e5e20779 Faidon Liambotis
    @param node: node whose info to return
1309 e5e20779 Faidon Liambotis

1310 e5e20779 Faidon Liambotis
    @rtype: dict
1311 e5e20779 Faidon Liambotis
    @return: info about the node
1312 e5e20779 Faidon Liambotis

1313 e5e20779 Faidon Liambotis
    """
1314 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1315 e5e20779 Faidon Liambotis
                             "/%s/nodes/%s" % (GANETI_RAPI_VERSION, node),
1316 e5e20779 Faidon Liambotis
                             None, None)
1317 e5e20779 Faidon Liambotis
1318 e5e20779 Faidon Liambotis
  def EvacuateNode(self, node, iallocator=None, remote_node=None,
1319 a3ad611d Dimitris Aragiorgis
                   dry_run=False, early_release=None,
1320 a3ad611d Dimitris Aragiorgis
                   mode=None, accept_old=False):
1321 e5e20779 Faidon Liambotis
    """Evacuates instances from a Ganeti node.
1322 e5e20779 Faidon Liambotis

1323 e5e20779 Faidon Liambotis
    @type node: str
1324 e5e20779 Faidon Liambotis
    @param node: node to evacuate
1325 e5e20779 Faidon Liambotis
    @type iallocator: str or None
1326 e5e20779 Faidon Liambotis
    @param iallocator: instance allocator to use
1327 e5e20779 Faidon Liambotis
    @type remote_node: str
1328 e5e20779 Faidon Liambotis
    @param remote_node: node to evaucate to
1329 e5e20779 Faidon Liambotis
    @type dry_run: bool
1330 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1331 e5e20779 Faidon Liambotis
    @type early_release: bool
1332 e5e20779 Faidon Liambotis
    @param early_release: whether to enable parallelization
1333 a3ad611d Dimitris Aragiorgis
    @type mode: string
1334 a3ad611d Dimitris Aragiorgis
    @param mode: Node evacuation mode
1335 a3ad611d Dimitris Aragiorgis
    @type accept_old: bool
1336 a3ad611d Dimitris Aragiorgis
    @param accept_old: Whether caller is ready to accept old-style (pre-2.5)
1337 a3ad611d Dimitris Aragiorgis
        results
1338 e5e20779 Faidon Liambotis

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

1345 e5e20779 Faidon Liambotis
    @raises GanetiApiError: if an iallocator and remote_node are both
1346 e5e20779 Faidon Liambotis
        specified
1347 e5e20779 Faidon Liambotis

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

1389 e5e20779 Faidon Liambotis
    @type node: str
1390 e5e20779 Faidon Liambotis
    @param node: node to migrate
1391 e5e20779 Faidon Liambotis
    @type mode: string
1392 e5e20779 Faidon Liambotis
    @param mode: if passed, it will overwrite the live migration type,
1393 e5e20779 Faidon Liambotis
        otherwise the hypervisor default will be used
1394 e5e20779 Faidon Liambotis
    @type dry_run: bool
1395 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1396 a3ad611d Dimitris Aragiorgis
    @type iallocator: string
1397 a3ad611d Dimitris Aragiorgis
    @param iallocator: instance allocator to use
1398 a3ad611d Dimitris Aragiorgis
    @type target_node: string
1399 a3ad611d Dimitris Aragiorgis
    @param target_node: Target node for shared-storage instances
1400 e5e20779 Faidon Liambotis

1401 a3ad611d Dimitris Aragiorgis
    @rtype: string
1402 e5e20779 Faidon Liambotis
    @return: job id
1403 e5e20779 Faidon Liambotis

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

1435 e5e20779 Faidon Liambotis
    @type node: str
1436 e5e20779 Faidon Liambotis
    @param node: node whose role to return
1437 e5e20779 Faidon Liambotis

1438 e5e20779 Faidon Liambotis
    @rtype: str
1439 e5e20779 Faidon Liambotis
    @return: the current role for a node
1440 e5e20779 Faidon Liambotis

1441 e5e20779 Faidon Liambotis
    """
1442 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1443 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1444 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1445 e5e20779 Faidon Liambotis
1446 a3ad611d Dimitris Aragiorgis
  def SetNodeRole(self, node, role, force=False, auto_promote=None):
1447 e5e20779 Faidon Liambotis
    """Sets the role for a node.
1448 e5e20779 Faidon Liambotis

1449 e5e20779 Faidon Liambotis
    @type node: str
1450 e5e20779 Faidon Liambotis
    @param node: the node whose role to set
1451 e5e20779 Faidon Liambotis
    @type role: str
1452 e5e20779 Faidon Liambotis
    @param role: the role to set for the node
1453 e5e20779 Faidon Liambotis
    @type force: bool
1454 e5e20779 Faidon Liambotis
    @param force: whether to force the role change
1455 a3ad611d Dimitris Aragiorgis
    @type auto_promote: bool
1456 a3ad611d Dimitris Aragiorgis
    @param auto_promote: Whether node(s) should be promoted to master candidate
1457 a3ad611d Dimitris Aragiorgis
                         if necessary
1458 e5e20779 Faidon Liambotis

1459 a3ad611d Dimitris Aragiorgis
    @rtype: string
1460 e5e20779 Faidon Liambotis
    @return: job id
1461 e5e20779 Faidon Liambotis

1462 e5e20779 Faidon Liambotis
    """
1463 a3ad611d Dimitris Aragiorgis
    query = []
1464 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1465 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, auto_promote is not None, ("auto-promote", auto_promote))
1466 e5e20779 Faidon Liambotis
1467 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1468 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/role" %
1469 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, role)
1470 e5e20779 Faidon Liambotis
1471 a3ad611d Dimitris Aragiorgis
  def PowercycleNode(self, node, force=False):
1472 a3ad611d Dimitris Aragiorgis
    """Powercycles a node.
1473 a3ad611d Dimitris Aragiorgis

1474 a3ad611d Dimitris Aragiorgis
    @type node: string
1475 a3ad611d Dimitris Aragiorgis
    @param node: Node name
1476 a3ad611d Dimitris Aragiorgis
    @type force: bool
1477 a3ad611d Dimitris Aragiorgis
    @param force: Whether to force the operation
1478 a3ad611d Dimitris Aragiorgis
    @rtype: string
1479 a3ad611d Dimitris Aragiorgis
    @return: job id
1480 a3ad611d Dimitris Aragiorgis

1481 a3ad611d Dimitris Aragiorgis
    """
1482 a3ad611d Dimitris Aragiorgis
    query = []
1483 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1484 a3ad611d Dimitris Aragiorgis
1485 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
1486 a3ad611d Dimitris Aragiorgis
                             ("/%s/nodes/%s/powercycle" %
1487 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, node)), query, None)
1488 a3ad611d Dimitris Aragiorgis
1489 a3ad611d Dimitris Aragiorgis
  def ModifyNode(self, node, **kwargs):
1490 a3ad611d Dimitris Aragiorgis
    """Modifies a node.
1491 a3ad611d Dimitris Aragiorgis

1492 a3ad611d Dimitris Aragiorgis
    More details for parameters can be found in the RAPI documentation.
1493 a3ad611d Dimitris Aragiorgis

1494 a3ad611d Dimitris Aragiorgis
    @type node: string
1495 a3ad611d Dimitris Aragiorgis
    @param node: Node name
1496 a3ad611d Dimitris Aragiorgis
    @rtype: string
1497 a3ad611d Dimitris Aragiorgis
    @return: job id
1498 a3ad611d Dimitris Aragiorgis

1499 a3ad611d Dimitris Aragiorgis
    """
1500 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST,
1501 a3ad611d Dimitris Aragiorgis
                             ("/%s/nodes/%s/modify" %
1502 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, node)), None, kwargs)
1503 a3ad611d Dimitris Aragiorgis
1504 e5e20779 Faidon Liambotis
  def GetNodeStorageUnits(self, node, storage_type, output_fields):
1505 e5e20779 Faidon Liambotis
    """Gets the storage units for a node.
1506 e5e20779 Faidon Liambotis

1507 e5e20779 Faidon Liambotis
    @type node: str
1508 e5e20779 Faidon Liambotis
    @param node: the node whose storage units to return
1509 e5e20779 Faidon Liambotis
    @type storage_type: str
1510 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to return
1511 e5e20779 Faidon Liambotis
    @type output_fields: str
1512 e5e20779 Faidon Liambotis
    @param output_fields: storage type fields to return
1513 e5e20779 Faidon Liambotis

1514 a3ad611d Dimitris Aragiorgis
    @rtype: string
1515 e5e20779 Faidon Liambotis
    @return: job id where results can be retrieved
1516 e5e20779 Faidon Liambotis

1517 e5e20779 Faidon Liambotis
    """
1518 e5e20779 Faidon Liambotis
    query = [
1519 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1520 e5e20779 Faidon Liambotis
      ("output_fields", output_fields),
1521 e5e20779 Faidon Liambotis
      ]
1522 e5e20779 Faidon Liambotis
1523 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1524 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage" %
1525 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1526 e5e20779 Faidon Liambotis
1527 e5e20779 Faidon Liambotis
  def ModifyNodeStorageUnits(self, node, storage_type, name, allocatable=None):
1528 e5e20779 Faidon Liambotis
    """Modifies parameters of storage units on the node.
1529 e5e20779 Faidon Liambotis

1530 e5e20779 Faidon Liambotis
    @type node: str
1531 e5e20779 Faidon Liambotis
    @param node: node whose storage units to modify
1532 e5e20779 Faidon Liambotis
    @type storage_type: str
1533 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to modify
1534 e5e20779 Faidon Liambotis
    @type name: str
1535 e5e20779 Faidon Liambotis
    @param name: name of the storage unit
1536 e5e20779 Faidon Liambotis
    @type allocatable: bool or None
1537 e5e20779 Faidon Liambotis
    @param allocatable: Whether to set the "allocatable" flag on the storage
1538 e5e20779 Faidon Liambotis
                        unit (None=no modification, True=set, False=unset)
1539 e5e20779 Faidon Liambotis

1540 a3ad611d Dimitris Aragiorgis
    @rtype: string
1541 e5e20779 Faidon Liambotis
    @return: job id
1542 e5e20779 Faidon Liambotis

1543 e5e20779 Faidon Liambotis
    """
1544 e5e20779 Faidon Liambotis
    query = [
1545 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1546 e5e20779 Faidon Liambotis
      ("name", name),
1547 e5e20779 Faidon Liambotis
      ]
1548 e5e20779 Faidon Liambotis
1549 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, allocatable is not None, ("allocatable", allocatable))
1550 e5e20779 Faidon Liambotis
1551 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1552 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/modify" %
1553 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1554 e5e20779 Faidon Liambotis
1555 e5e20779 Faidon Liambotis
  def RepairNodeStorageUnits(self, node, storage_type, name):
1556 e5e20779 Faidon Liambotis
    """Repairs a storage unit on the node.
1557 e5e20779 Faidon Liambotis

1558 e5e20779 Faidon Liambotis
    @type node: str
1559 e5e20779 Faidon Liambotis
    @param node: node whose storage units to repair
1560 e5e20779 Faidon Liambotis
    @type storage_type: str
1561 e5e20779 Faidon Liambotis
    @param storage_type: storage type to repair
1562 e5e20779 Faidon Liambotis
    @type name: str
1563 e5e20779 Faidon Liambotis
    @param name: name of the storage unit to repair
1564 e5e20779 Faidon Liambotis

1565 a3ad611d Dimitris Aragiorgis
    @rtype: string
1566 e5e20779 Faidon Liambotis
    @return: job id
1567 e5e20779 Faidon Liambotis

1568 e5e20779 Faidon Liambotis
    """
1569 e5e20779 Faidon Liambotis
    query = [
1570 e5e20779 Faidon Liambotis
      ("storage_type", storage_type),
1571 e5e20779 Faidon Liambotis
      ("name", name),
1572 e5e20779 Faidon Liambotis
      ]
1573 e5e20779 Faidon Liambotis
1574 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1575 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/storage/repair" %
1576 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1577 e5e20779 Faidon Liambotis
1578 e5e20779 Faidon Liambotis
  def GetNodeTags(self, node):
1579 e5e20779 Faidon Liambotis
    """Gets the tags for a node.
1580 e5e20779 Faidon Liambotis

1581 e5e20779 Faidon Liambotis
    @type node: str
1582 e5e20779 Faidon Liambotis
    @param node: node whose tags to return
1583 e5e20779 Faidon Liambotis

1584 e5e20779 Faidon Liambotis
    @rtype: list of str
1585 e5e20779 Faidon Liambotis
    @return: tags for the node
1586 e5e20779 Faidon Liambotis

1587 e5e20779 Faidon Liambotis
    """
1588 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_GET,
1589 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1590 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), None, None)
1591 e5e20779 Faidon Liambotis
1592 e5e20779 Faidon Liambotis
  def AddNodeTags(self, node, tags, dry_run=False):
1593 e5e20779 Faidon Liambotis
    """Adds tags to a node.
1594 e5e20779 Faidon Liambotis

1595 e5e20779 Faidon Liambotis
    @type node: str
1596 e5e20779 Faidon Liambotis
    @param node: node to add tags to
1597 e5e20779 Faidon Liambotis
    @type tags: list of str
1598 e5e20779 Faidon Liambotis
    @param tags: tags to add to the node
1599 e5e20779 Faidon Liambotis
    @type dry_run: bool
1600 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1601 e5e20779 Faidon Liambotis

1602 a3ad611d Dimitris Aragiorgis
    @rtype: string
1603 e5e20779 Faidon Liambotis
    @return: job id
1604 e5e20779 Faidon Liambotis

1605 e5e20779 Faidon Liambotis
    """
1606 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1607 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1608 e5e20779 Faidon Liambotis
1609 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_PUT,
1610 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1611 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, tags)
1612 e5e20779 Faidon Liambotis
1613 e5e20779 Faidon Liambotis
  def DeleteNodeTags(self, node, tags, dry_run=False):
1614 e5e20779 Faidon Liambotis
    """Delete tags from a node.
1615 e5e20779 Faidon Liambotis

1616 e5e20779 Faidon Liambotis
    @type node: str
1617 e5e20779 Faidon Liambotis
    @param node: node to remove tags from
1618 e5e20779 Faidon Liambotis
    @type tags: list of str
1619 e5e20779 Faidon Liambotis
    @param tags: tags to remove from the node
1620 e5e20779 Faidon Liambotis
    @type dry_run: bool
1621 e5e20779 Faidon Liambotis
    @param dry_run: whether to perform a dry run
1622 e5e20779 Faidon Liambotis

1623 a3ad611d Dimitris Aragiorgis
    @rtype: string
1624 e5e20779 Faidon Liambotis
    @return: job id
1625 e5e20779 Faidon Liambotis

1626 e5e20779 Faidon Liambotis
    """
1627 e5e20779 Faidon Liambotis
    query = [("tag", t) for t in tags]
1628 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1629 e5e20779 Faidon Liambotis
1630 e5e20779 Faidon Liambotis
    return self._SendRequest(HTTP_DELETE,
1631 e5e20779 Faidon Liambotis
                             ("/%s/nodes/%s/tags" %
1632 e5e20779 Faidon Liambotis
                              (GANETI_RAPI_VERSION, node)), query, None)
1633 067dda99 Vangelis Koukis
1634 a3ad611d Dimitris Aragiorgis
  def GetNetworks(self, bulk=False):
1635 a3ad611d Dimitris Aragiorgis
    """Gets all networks in the cluster.
1636 a3ad611d Dimitris Aragiorgis

1637 a3ad611d Dimitris Aragiorgis
    @type bulk: bool
1638 a3ad611d Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1639 a3ad611d Dimitris Aragiorgis

1640 a3ad611d Dimitris Aragiorgis
    @rtype: list of dict or str
1641 a3ad611d Dimitris Aragiorgis
    @return: if bulk is true, a list of dictionaries with info about all
1642 a3ad611d Dimitris Aragiorgis
        networks in the cluster, else a list of names of those networks
1643 a3ad611d Dimitris Aragiorgis

1644 a3ad611d Dimitris Aragiorgis
    """
1645 a3ad611d Dimitris Aragiorgis
    query = []
1646 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1647 a3ad611d Dimitris Aragiorgis
1648 a3ad611d Dimitris Aragiorgis
    networks = self._SendRequest(HTTP_GET, "/%s/networks" % GANETI_RAPI_VERSION,
1649 a3ad611d Dimitris Aragiorgis
                               query, None)
1650 a3ad611d Dimitris Aragiorgis
    if bulk:
1651 a3ad611d Dimitris Aragiorgis
      return networks
1652 a3ad611d Dimitris Aragiorgis
    else:
1653 a3ad611d Dimitris Aragiorgis
      return [n["name"] for n in networks]
1654 a3ad611d Dimitris Aragiorgis
1655 a3ad611d Dimitris Aragiorgis
  def GetNetwork(self, network):
1656 a3ad611d Dimitris Aragiorgis
    """Gets information about a network.
1657 a3ad611d Dimitris Aragiorgis

1658 a3ad611d Dimitris Aragiorgis
    @type group: str
1659 a3ad611d Dimitris Aragiorgis
    @param group: name of the network whose info to return
1660 a3ad611d Dimitris Aragiorgis

1661 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1662 a3ad611d Dimitris Aragiorgis
    @return: info about the network
1663 a3ad611d Dimitris Aragiorgis

1664 a3ad611d Dimitris Aragiorgis
    """
1665 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1666 a3ad611d Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1667 a3ad611d Dimitris Aragiorgis
                             None, None)
1668 a3ad611d Dimitris Aragiorgis
1669 a3ad611d Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1670 3f68fa52 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None,
1671 a406663f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=[],
1672 a406663f Dimitris Aragiorgis
                    conflicts_check=False, dry_run=False):
1673 a3ad611d Dimitris Aragiorgis
    """Creates a new network.
1674 a3ad611d Dimitris Aragiorgis

1675 a3ad611d Dimitris Aragiorgis
    @type name: str
1676 a3ad611d Dimitris Aragiorgis
    @param name: the name of network to create
1677 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1678 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1679 a3ad611d Dimitris Aragiorgis

1680 a3ad611d Dimitris Aragiorgis
    @rtype: string
1681 a3ad611d Dimitris Aragiorgis
    @return: job id
1682 a3ad611d Dimitris Aragiorgis

1683 a3ad611d Dimitris Aragiorgis
    """
1684 a3ad611d Dimitris Aragiorgis
    query = []
1685 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1686 a3ad611d Dimitris Aragiorgis
1687 a3ad611d Dimitris Aragiorgis
    body = {
1688 a3ad611d Dimitris Aragiorgis
      "network_name": network_name,
1689 a3ad611d Dimitris Aragiorgis
      "gateway": gateway,
1690 a3ad611d Dimitris Aragiorgis
      "network": network,
1691 a3ad611d Dimitris Aragiorgis
      "gateway6": gateway6,
1692 a3ad611d Dimitris Aragiorgis
      "network6": network6,
1693 a3ad611d Dimitris Aragiorgis
      "mac_prefix": mac_prefix,
1694 a406663f Dimitris Aragiorgis
      "add_reserved_ips": add_reserved_ips,
1695 6de25206 Dimitris Aragiorgis
      "conflicts_check": conflicts_check,
1696 a406663f Dimitris Aragiorgis
      "tags": tags,
1697 a3ad611d Dimitris Aragiorgis
      }
1698 a3ad611d Dimitris Aragiorgis
1699 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
1700 a3ad611d Dimitris Aragiorgis
                             query, body)
1701 a3ad611d Dimitris Aragiorgis
1702 6de25206 Dimitris Aragiorgis
  def ConnectNetwork(self, network_name, group_name, mode, link,
1703 a406663f Dimitris Aragiorgis
                     conflicts_check=False, depends=None, dry_run=False):
1704 a3ad611d Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1705 a3ad611d Dimitris Aragiorgis

1706 a3ad611d Dimitris Aragiorgis
    """
1707 a3ad611d Dimitris Aragiorgis
    body = {
1708 a3ad611d Dimitris Aragiorgis
      "group_name": group_name,
1709 a3ad611d Dimitris Aragiorgis
      "network_mode": mode,
1710 6de25206 Dimitris Aragiorgis
      "network_link": link,
1711 6de25206 Dimitris Aragiorgis
      "conflicts_check": conflicts_check,
1712 a3ad611d Dimitris Aragiorgis
      }
1713 a3ad611d Dimitris Aragiorgis
1714 a3ad611d Dimitris Aragiorgis
    if depends:
1715 99af08a4 Christos Stavrakakis
      body['depends'] = depends
1716 a3ad611d Dimitris Aragiorgis
1717 a406663f Dimitris Aragiorgis
    query = []
1718 a406663f Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1719 a3ad611d Dimitris Aragiorgis
1720 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1721 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s/connect" %
1722 a406663f Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1723 a3ad611d Dimitris Aragiorgis
1724 a406663f Dimitris Aragiorgis
  def DisconnectNetwork(self, network_name, group_name,
1725 a406663f Dimitris Aragiorgis
                        depends=None, dry_run=False):
1726 a3ad611d Dimitris Aragiorgis
    """Connects a Network to a NodeGroup with the given netparams
1727 a3ad611d Dimitris Aragiorgis

1728 a3ad611d Dimitris Aragiorgis
    """
1729 a3ad611d Dimitris Aragiorgis
    body = {
1730 a3ad611d Dimitris Aragiorgis
      "group_name": group_name
1731 a3ad611d Dimitris Aragiorgis
      }
1732 a3ad611d Dimitris Aragiorgis
1733 a3ad611d Dimitris Aragiorgis
    if depends:
1734 99af08a4 Christos Stavrakakis
      body['depends'] = depends
1735 a3ad611d Dimitris Aragiorgis
1736 a406663f Dimitris Aragiorgis
    query = []
1737 a406663f Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1738 a406663f Dimitris Aragiorgis
1739 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1740 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s/disconnect" %
1741 a406663f Dimitris Aragiorgis
                             (GANETI_RAPI_VERSION, network_name)), query, body)
1742 a406663f Dimitris Aragiorgis
1743 a406663f Dimitris Aragiorgis
1744 a406663f Dimitris Aragiorgis
  def ModifyNetwork(self, network, **kwargs):
1745 a406663f Dimitris Aragiorgis
    """Modifies a network.
1746 a406663f Dimitris Aragiorgis

1747 a406663f Dimitris Aragiorgis
    More details for parameters can be found in the RAPI documentation.
1748 a3ad611d Dimitris Aragiorgis

1749 a406663f Dimitris Aragiorgis
    @type network: string
1750 a406663f Dimitris Aragiorgis
    @param network: Network name
1751 a406663f Dimitris Aragiorgis
    @rtype: string
1752 a406663f Dimitris Aragiorgis
    @return: job id
1753 a3ad611d Dimitris Aragiorgis

1754 a406663f Dimitris Aragiorgis
    """
1755 a406663f Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1756 a406663f Dimitris Aragiorgis
                             ("/%s/networks/%s/modify" %
1757 a406663f Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, kwargs)
1758 a406663f Dimitris Aragiorgis
1759 a406663f Dimitris Aragiorgis
  def DeleteNetwork(self, network, depends=None, dry_run=False):
1760 a3ad611d Dimitris Aragiorgis
    """Deletes a network.
1761 a3ad611d Dimitris Aragiorgis

1762 a3ad611d Dimitris Aragiorgis
    @type group: str
1763 a3ad611d Dimitris Aragiorgis
    @param group: the network to delete
1764 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1765 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1766 a3ad611d Dimitris Aragiorgis

1767 a3ad611d Dimitris Aragiorgis
    @rtype: string
1768 a3ad611d Dimitris Aragiorgis
    @return: job id
1769 a3ad611d Dimitris Aragiorgis

1770 a3ad611d Dimitris Aragiorgis
    """
1771 a3ad611d Dimitris Aragiorgis
    body = {}
1772 a3ad611d Dimitris Aragiorgis
    if depends:
1773 99af08a4 Christos Stavrakakis
      body['depends'] = depends
1774 a3ad611d Dimitris Aragiorgis
1775 a406663f Dimitris Aragiorgis
    query = []
1776 a406663f Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1777 a3ad611d Dimitris Aragiorgis
1778 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1779 a3ad611d Dimitris Aragiorgis
                             ("/%s/networks/%s" %
1780 a406663f Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, body)
1781 a406663f Dimitris Aragiorgis
1782 a406663f Dimitris Aragiorgis
  def GetNetworkTags(self, network):
1783 a406663f Dimitris Aragiorgis
    """Gets tags for a network.
1784 a406663f Dimitris Aragiorgis

1785 a406663f Dimitris Aragiorgis
    @type network: string
1786 a406663f Dimitris Aragiorgis
    @param network: Node group whose tags to return
1787 a406663f Dimitris Aragiorgis

1788 a406663f Dimitris Aragiorgis
    @rtype: list of strings
1789 a406663f Dimitris Aragiorgis
    @return: tags for the network
1790 a406663f Dimitris Aragiorgis

1791 a406663f Dimitris Aragiorgis
    """
1792 a406663f Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1793 a406663f Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1794 a406663f Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), None, None)
1795 a406663f Dimitris Aragiorgis
1796 a406663f Dimitris Aragiorgis
  def AddNetworkTags(self, network, tags, dry_run=False):
1797 a406663f Dimitris Aragiorgis
    """Adds tags to a network.
1798 a406663f Dimitris Aragiorgis

1799 a406663f Dimitris Aragiorgis
    @type network: str
1800 a406663f Dimitris Aragiorgis
    @param network: network to add tags to
1801 a406663f Dimitris Aragiorgis
    @type tags: list of string
1802 a406663f Dimitris Aragiorgis
    @param tags: tags to add to the network
1803 a406663f Dimitris Aragiorgis
    @type dry_run: bool
1804 a406663f Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1805 a406663f Dimitris Aragiorgis

1806 a406663f Dimitris Aragiorgis
    @rtype: string
1807 a406663f Dimitris Aragiorgis
    @return: job id
1808 a406663f Dimitris Aragiorgis

1809 a406663f Dimitris Aragiorgis
    """
1810 a406663f Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1811 a406663f Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1812 a406663f Dimitris Aragiorgis
1813 a406663f Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
1814 a406663f Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1815 a406663f Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1816 a406663f Dimitris Aragiorgis
1817 a406663f Dimitris Aragiorgis
  def DeleteNetworkTags(self, network, tags, dry_run=False):
1818 a406663f Dimitris Aragiorgis
    """Deletes tags from a network.
1819 a406663f Dimitris Aragiorgis

1820 a406663f Dimitris Aragiorgis
    @type network: str
1821 a406663f Dimitris Aragiorgis
    @param network: network to delete tags from
1822 a406663f Dimitris Aragiorgis
    @type tags: list of string
1823 a406663f Dimitris Aragiorgis
    @param tags: tags to delete
1824 a406663f Dimitris Aragiorgis
    @type dry_run: bool
1825 a406663f Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1826 a406663f Dimitris Aragiorgis
    @rtype: string
1827 a406663f Dimitris Aragiorgis
    @return: job id
1828 a406663f Dimitris Aragiorgis

1829 a406663f Dimitris Aragiorgis
    """
1830 a406663f Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
1831 a406663f Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1832 a406663f Dimitris Aragiorgis
1833 a406663f Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
1834 a406663f Dimitris Aragiorgis
                             ("/%s/networks/%s/tags" %
1835 a406663f Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, network)), query, None)
1836 a406663f Dimitris Aragiorgis
1837 a3ad611d Dimitris Aragiorgis
1838 067dda99 Vangelis Koukis
  def GetGroups(self, bulk=False):
1839 067dda99 Vangelis Koukis
    """Gets all node groups in the cluster.
1840 067dda99 Vangelis Koukis

1841 067dda99 Vangelis Koukis
    @type bulk: bool
1842 067dda99 Vangelis Koukis
    @param bulk: whether to return all information about the groups
1843 067dda99 Vangelis Koukis

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

1848 067dda99 Vangelis Koukis
    """
1849 067dda99 Vangelis Koukis
    query = []
1850 a3ad611d Dimitris Aragiorgis
    _AppendIf(query, bulk, ("bulk", 1))
1851 067dda99 Vangelis Koukis
1852 067dda99 Vangelis Koukis
    groups = self._SendRequest(HTTP_GET, "/%s/groups" % GANETI_RAPI_VERSION,
1853 067dda99 Vangelis Koukis
                               query, None)
1854 067dda99 Vangelis Koukis
    if bulk:
1855 067dda99 Vangelis Koukis
      return groups
1856 067dda99 Vangelis Koukis
    else:
1857 067dda99 Vangelis Koukis
      return [g["name"] for g in groups]
1858 067dda99 Vangelis Koukis
1859 067dda99 Vangelis Koukis
  def GetGroup(self, group):
1860 067dda99 Vangelis Koukis
    """Gets information about a node group.
1861 067dda99 Vangelis Koukis

1862 067dda99 Vangelis Koukis
    @type group: str
1863 067dda99 Vangelis Koukis
    @param group: name of the node group whose info to return
1864 067dda99 Vangelis Koukis

1865 067dda99 Vangelis Koukis
    @rtype: dict
1866 067dda99 Vangelis Koukis
    @return: info about the node group
1867 067dda99 Vangelis Koukis

1868 067dda99 Vangelis Koukis
    """
1869 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_GET,
1870 067dda99 Vangelis Koukis
                             "/%s/groups/%s" % (GANETI_RAPI_VERSION, group),
1871 067dda99 Vangelis Koukis
                             None, None)
1872 067dda99 Vangelis Koukis
1873 067dda99 Vangelis Koukis
  def CreateGroup(self, name, alloc_policy=None, dry_run=False):
1874 067dda99 Vangelis Koukis
    """Creates a new node group.
1875 067dda99 Vangelis Koukis

1876 067dda99 Vangelis Koukis
    @type name: str
1877 067dda99 Vangelis Koukis
    @param name: the name of node group to create
1878 067dda99 Vangelis Koukis
    @type alloc_policy: str
1879 067dda99 Vangelis Koukis
    @param alloc_policy: the desired allocation policy for the group, if any
1880 067dda99 Vangelis Koukis
    @type dry_run: bool
1881 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1882 067dda99 Vangelis Koukis

1883 a3ad611d Dimitris Aragiorgis
    @rtype: string
1884 067dda99 Vangelis Koukis
    @return: job id
1885 067dda99 Vangelis Koukis

1886 067dda99 Vangelis Koukis
    """
1887 067dda99 Vangelis Koukis
    query = []
1888 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1889 067dda99 Vangelis Koukis
1890 067dda99 Vangelis Koukis
    body = {
1891 067dda99 Vangelis Koukis
      "name": name,
1892 067dda99 Vangelis Koukis
      "alloc_policy": alloc_policy
1893 067dda99 Vangelis Koukis
      }
1894 067dda99 Vangelis Koukis
1895 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_POST, "/%s/groups" % GANETI_RAPI_VERSION,
1896 067dda99 Vangelis Koukis
                             query, body)
1897 067dda99 Vangelis Koukis
1898 067dda99 Vangelis Koukis
  def ModifyGroup(self, group, **kwargs):
1899 067dda99 Vangelis Koukis
    """Modifies a node group.
1900 067dda99 Vangelis Koukis

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

1903 067dda99 Vangelis Koukis
    @type group: string
1904 067dda99 Vangelis Koukis
    @param group: Node group name
1905 a3ad611d Dimitris Aragiorgis
    @rtype: string
1906 067dda99 Vangelis Koukis
    @return: job id
1907 067dda99 Vangelis Koukis

1908 067dda99 Vangelis Koukis
    """
1909 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1910 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/modify" %
1911 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, kwargs)
1912 067dda99 Vangelis Koukis
1913 067dda99 Vangelis Koukis
  def DeleteGroup(self, group, dry_run=False):
1914 067dda99 Vangelis Koukis
    """Deletes a node group.
1915 067dda99 Vangelis Koukis

1916 067dda99 Vangelis Koukis
    @type group: str
1917 067dda99 Vangelis Koukis
    @param group: the node group to delete
1918 067dda99 Vangelis Koukis
    @type dry_run: bool
1919 067dda99 Vangelis Koukis
    @param dry_run: whether to peform a dry run
1920 067dda99 Vangelis Koukis

1921 a3ad611d Dimitris Aragiorgis
    @rtype: string
1922 067dda99 Vangelis Koukis
    @return: job id
1923 067dda99 Vangelis Koukis

1924 067dda99 Vangelis Koukis
    """
1925 067dda99 Vangelis Koukis
    query = []
1926 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1927 067dda99 Vangelis Koukis
1928 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_DELETE,
1929 067dda99 Vangelis Koukis
                             ("/%s/groups/%s" %
1930 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), query, None)
1931 067dda99 Vangelis Koukis
1932 067dda99 Vangelis Koukis
  def RenameGroup(self, group, new_name):
1933 067dda99 Vangelis Koukis
    """Changes the name of a node group.
1934 067dda99 Vangelis Koukis

1935 067dda99 Vangelis Koukis
    @type group: string
1936 067dda99 Vangelis Koukis
    @param group: Node group name
1937 067dda99 Vangelis Koukis
    @type new_name: string
1938 067dda99 Vangelis Koukis
    @param new_name: New node group name
1939 067dda99 Vangelis Koukis

1940 a3ad611d Dimitris Aragiorgis
    @rtype: string
1941 067dda99 Vangelis Koukis
    @return: job id
1942 067dda99 Vangelis Koukis

1943 067dda99 Vangelis Koukis
    """
1944 067dda99 Vangelis Koukis
    body = {
1945 067dda99 Vangelis Koukis
      "new_name": new_name,
1946 067dda99 Vangelis Koukis
      }
1947 067dda99 Vangelis Koukis
1948 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1949 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/rename" %
1950 067dda99 Vangelis Koukis
                              (GANETI_RAPI_VERSION, group)), None, body)
1951 067dda99 Vangelis Koukis
1952 067dda99 Vangelis Koukis
  def AssignGroupNodes(self, group, nodes, force=False, dry_run=False):
1953 067dda99 Vangelis Koukis
    """Assigns nodes to a group.
1954 067dda99 Vangelis Koukis

1955 067dda99 Vangelis Koukis
    @type group: string
1956 067dda99 Vangelis Koukis
    @param group: Node gropu name
1957 067dda99 Vangelis Koukis
    @type nodes: list of strings
1958 067dda99 Vangelis Koukis
    @param nodes: List of nodes to assign to the group
1959 067dda99 Vangelis Koukis

1960 a3ad611d Dimitris Aragiorgis
    @rtype: string
1961 067dda99 Vangelis Koukis
    @return: job id
1962 067dda99 Vangelis Koukis

1963 067dda99 Vangelis Koukis
    """
1964 067dda99 Vangelis Koukis
    query = []
1965 a3ad611d Dimitris Aragiorgis
    _AppendForceIf(query, force)
1966 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
1967 067dda99 Vangelis Koukis
1968 067dda99 Vangelis Koukis
    body = {
1969 067dda99 Vangelis Koukis
      "nodes": nodes,
1970 067dda99 Vangelis Koukis
      }
1971 067dda99 Vangelis Koukis
1972 067dda99 Vangelis Koukis
    return self._SendRequest(HTTP_PUT,
1973 067dda99 Vangelis Koukis
                             ("/%s/groups/%s/assign-nodes" %
1974 067dda99 Vangelis Koukis
                             (GANETI_RAPI_VERSION, group)), query, body)
1975 a3ad611d Dimitris Aragiorgis
1976 a3ad611d Dimitris Aragiorgis
  def GetGroupTags(self, group):
1977 a3ad611d Dimitris Aragiorgis
    """Gets tags for a node group.
1978 a3ad611d Dimitris Aragiorgis

1979 a3ad611d Dimitris Aragiorgis
    @type group: string
1980 a3ad611d Dimitris Aragiorgis
    @param group: Node group whose tags to return
1981 a3ad611d Dimitris Aragiorgis

1982 a3ad611d Dimitris Aragiorgis
    @rtype: list of strings
1983 a3ad611d Dimitris Aragiorgis
    @return: tags for the group
1984 a3ad611d Dimitris Aragiorgis

1985 a3ad611d Dimitris Aragiorgis
    """
1986 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1987 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
1988 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), None, None)
1989 a3ad611d Dimitris Aragiorgis
1990 a3ad611d Dimitris Aragiorgis
  def AddGroupTags(self, group, tags, dry_run=False):
1991 a3ad611d Dimitris Aragiorgis
    """Adds tags to a node group.
1992 a3ad611d Dimitris Aragiorgis

1993 a3ad611d Dimitris Aragiorgis
    @type group: str
1994 a3ad611d Dimitris Aragiorgis
    @param group: group to add tags to
1995 a3ad611d Dimitris Aragiorgis
    @type tags: list of string
1996 a3ad611d Dimitris Aragiorgis
    @param tags: tags to add to the group
1997 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1998 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
1999 a3ad611d Dimitris Aragiorgis

2000 a3ad611d Dimitris Aragiorgis
    @rtype: string
2001 a3ad611d Dimitris Aragiorgis
    @return: job id
2002 a3ad611d Dimitris Aragiorgis

2003 a3ad611d Dimitris Aragiorgis
    """
2004 a3ad611d Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
2005 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
2006 a3ad611d Dimitris Aragiorgis
2007 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
2008 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
2009 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), query, None)
2010 a3ad611d Dimitris Aragiorgis
2011 a3ad611d Dimitris Aragiorgis
  def DeleteGroupTags(self, group, tags, dry_run=False):
2012 a3ad611d Dimitris Aragiorgis
    """Deletes tags from a node group.
2013 a3ad611d Dimitris Aragiorgis

2014 a3ad611d Dimitris Aragiorgis
    @type group: str
2015 a3ad611d Dimitris Aragiorgis
    @param group: group to delete tags from
2016 a3ad611d Dimitris Aragiorgis
    @type tags: list of string
2017 a3ad611d Dimitris Aragiorgis
    @param tags: tags to delete
2018 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
2019 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to perform a dry run
2020 a3ad611d Dimitris Aragiorgis
    @rtype: string
2021 a3ad611d Dimitris Aragiorgis
    @return: job id
2022 a3ad611d Dimitris Aragiorgis

2023 a3ad611d Dimitris Aragiorgis
    """
2024 a3ad611d Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
2025 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
2026 a3ad611d Dimitris Aragiorgis
2027 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_DELETE,
2028 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
2029 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), query, None)
2030 a3ad611d Dimitris Aragiorgis
2031 a3ad611d Dimitris Aragiorgis
  def Query(self, what, fields, qfilter=None):
2032 a3ad611d Dimitris Aragiorgis
    """Retrieves information about resources.
2033 a3ad611d Dimitris Aragiorgis

2034 a3ad611d Dimitris Aragiorgis
    @type what: string
2035 a3ad611d Dimitris Aragiorgis
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2036 a3ad611d Dimitris Aragiorgis
    @type fields: list of string
2037 a3ad611d Dimitris Aragiorgis
    @param fields: Requested fields
2038 a3ad611d Dimitris Aragiorgis
    @type qfilter: None or list
2039 a3ad611d Dimitris Aragiorgis
    @param qfilter: Query filter
2040 a3ad611d Dimitris Aragiorgis

2041 a3ad611d Dimitris Aragiorgis
    @rtype: string
2042 a3ad611d Dimitris Aragiorgis
    @return: job id
2043 a3ad611d Dimitris Aragiorgis

2044 a3ad611d Dimitris Aragiorgis
    """
2045 a3ad611d Dimitris Aragiorgis
    body = {
2046 a3ad611d Dimitris Aragiorgis
      "fields": fields,
2047 a3ad611d Dimitris Aragiorgis
      }
2048 a3ad611d Dimitris Aragiorgis
2049 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, qfilter is not None, "qfilter", qfilter)
2050 a3ad611d Dimitris Aragiorgis
    # TODO: remove "filter" after 2.7
2051 a3ad611d Dimitris Aragiorgis
    _SetItemIf(body, qfilter is not None, "filter", qfilter)
2052 a3ad611d Dimitris Aragiorgis
2053 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
2054 a3ad611d Dimitris Aragiorgis
                             ("/%s/query/%s" %
2055 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, what)), None, body)
2056 a3ad611d Dimitris Aragiorgis
2057 a3ad611d Dimitris Aragiorgis
  def QueryFields(self, what, fields=None):
2058 a3ad611d Dimitris Aragiorgis
    """Retrieves available fields for a resource.
2059 a3ad611d Dimitris Aragiorgis

2060 a3ad611d Dimitris Aragiorgis
    @type what: string
2061 a3ad611d Dimitris Aragiorgis
    @param what: Resource name, one of L{constants.QR_VIA_RAPI}
2062 a3ad611d Dimitris Aragiorgis
    @type fields: list of string
2063 a3ad611d Dimitris Aragiorgis
    @param fields: Requested fields
2064 a3ad611d Dimitris Aragiorgis

2065 a3ad611d Dimitris Aragiorgis
    @rtype: string
2066 a3ad611d Dimitris Aragiorgis
    @return: job id
2067 a3ad611d Dimitris Aragiorgis

2068 a3ad611d Dimitris Aragiorgis
    """
2069 a3ad611d Dimitris Aragiorgis
    query = []
2070 a3ad611d Dimitris Aragiorgis
2071 a3ad611d Dimitris Aragiorgis
    if fields is not None:
2072 a3ad611d Dimitris Aragiorgis
      _AppendIf(query, True, ("fields", ",".join(fields)))
2073 a3ad611d Dimitris Aragiorgis
2074 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
2075 a3ad611d Dimitris Aragiorgis
                             ("/%s/query/%s/fields" %
2076 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, what)), query, None)