Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (60.3 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

395 e5e20779 Faidon Liambotis
    @type query: list of two-tuples
396 e5e20779 Faidon Liambotis
    @param query: Query arguments
397 e5e20779 Faidon Liambotis
    @rtype: list
398 e5e20779 Faidon Liambotis
    @return: Query list with encoded values
399 e5e20779 Faidon Liambotis

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

422 e5e20779 Faidon Liambotis
    This constructs a full URL, encodes and decodes HTTP bodies, and
423 e5e20779 Faidon Liambotis
    handles invalid responses in a pythonic way.
424 e5e20779 Faidon Liambotis

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

434 e5e20779 Faidon Liambotis
    @rtype: str
435 e5e20779 Faidon Liambotis
    @return: JSON-Decoded response
436 e5e20779 Faidon Liambotis

437 e5e20779 Faidon Liambotis
    @raises CertificateError: If an invalid SSL certificate is found
438 e5e20779 Faidon Liambotis
    @raises GanetiApiError: If an invalid response is returned
439 e5e20779 Faidon Liambotis

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

511 e5e20779 Faidon Liambotis
    @rtype: int
512 e5e20779 Faidon Liambotis
    @return: Ganeti Remote API version
513 e5e20779 Faidon Liambotis

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

520 e5e20779 Faidon Liambotis
    @rtype: list
521 e5e20779 Faidon Liambotis
    @return: List of optional features
522 e5e20779 Faidon Liambotis

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

537 e5e20779 Faidon Liambotis
    @rtype: list of str
538 e5e20779 Faidon Liambotis
    @return: operating systems
539 e5e20779 Faidon Liambotis

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

547 e5e20779 Faidon Liambotis
    @rtype: dict
548 e5e20779 Faidon Liambotis
    @return: information about the cluster
549 e5e20779 Faidon Liambotis

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

557 a3ad611d Dimitris Aragiorgis
    @rtype: string
558 067dda99 Vangelis Koukis
    @return: job id
559 067dda99 Vangelis Koukis

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

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

570 a3ad611d Dimitris Aragiorgis
    @rtype: string
571 067dda99 Vangelis Koukis
    @return: job id
572 067dda99 Vangelis Koukis

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

582 e5e20779 Faidon Liambotis
    @rtype: list of str
583 e5e20779 Faidon Liambotis
    @return: cluster tags
584 e5e20779 Faidon Liambotis

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

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

597 a3ad611d Dimitris Aragiorgis
    @rtype: string
598 e5e20779 Faidon Liambotis
    @return: job id
599 e5e20779 Faidon Liambotis

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

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

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

627 e5e20779 Faidon Liambotis
    @type bulk: bool
628 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
629 e5e20779 Faidon Liambotis

630 e5e20779 Faidon Liambotis
    @rtype: list of dict or list of str
631 e5e20779 Faidon Liambotis
    @return: if bulk is True, info about the instances, else a list of instances
632 e5e20779 Faidon Liambotis

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

648 e5e20779 Faidon Liambotis
    @type instance: str
649 e5e20779 Faidon Liambotis
    @param instance: instance whose info to return
650 e5e20779 Faidon Liambotis

651 e5e20779 Faidon Liambotis
    @rtype: dict
652 e5e20779 Faidon Liambotis
    @return: info about the instance
653 e5e20779 Faidon Liambotis

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

662 e5e20779 Faidon Liambotis
    @type instance: string
663 e5e20779 Faidon Liambotis
    @param instance: Instance name
664 e5e20779 Faidon Liambotis
    @rtype: string
665 e5e20779 Faidon Liambotis
    @return: Job ID
666 e5e20779 Faidon Liambotis

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

681 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
682 e5e20779 Faidon Liambotis

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

697 a3ad611d Dimitris Aragiorgis
    @rtype: string
698 e5e20779 Faidon Liambotis
    @return: job id
699 e5e20779 Faidon Liambotis

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

733 e5e20779 Faidon Liambotis
    @type instance: str
734 e5e20779 Faidon Liambotis
    @param instance: the instance to delete
735 e5e20779 Faidon Liambotis

736 a3ad611d Dimitris Aragiorgis
    @rtype: string
737 e5e20779 Faidon Liambotis
    @return: job id
738 e5e20779 Faidon Liambotis

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

750 e5e20779 Faidon Liambotis
    More details for parameters can be found in the RAPI documentation.
751 e5e20779 Faidon Liambotis

752 e5e20779 Faidon Liambotis
    @type instance: string
753 e5e20779 Faidon Liambotis
    @param instance: Instance name
754 a3ad611d Dimitris Aragiorgis
    @rtype: string
755 e5e20779 Faidon Liambotis
    @return: job id
756 e5e20779 Faidon Liambotis

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

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

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

785 067dda99 Vangelis Koukis
    @type instance: string
786 067dda99 Vangelis Koukis
    @param instance: Instance name
787 a3ad611d Dimitris Aragiorgis
    @rtype: string
788 067dda99 Vangelis Koukis
    @return: job id
789 067dda99 Vangelis Koukis

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

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

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

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

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

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

847 e5e20779 Faidon Liambotis
    @type instance: str
848 e5e20779 Faidon Liambotis
    @param instance: instance whose tags to return
849 e5e20779 Faidon Liambotis

850 e5e20779 Faidon Liambotis
    @rtype: list of str
851 e5e20779 Faidon Liambotis
    @return: tags for the instance
852 e5e20779 Faidon Liambotis

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

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

868 a3ad611d Dimitris Aragiorgis
    @rtype: string
869 e5e20779 Faidon Liambotis
    @return: job id
870 e5e20779 Faidon Liambotis

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

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

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

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

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

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

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

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

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

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

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

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

1023 a3ad611d Dimitris Aragiorgis
    @rtype: string
1024 e5e20779 Faidon Liambotis
    @return: job id
1025 e5e20779 Faidon Liambotis

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

1047 e5e20779 Faidon Liambotis
    @type instance: string
1048 e5e20779 Faidon Liambotis
    @param instance: Instance name
1049 e5e20779 Faidon Liambotis
    @type mode: string
1050 e5e20779 Faidon Liambotis
    @param mode: Export mode
1051 e5e20779 Faidon Liambotis
    @rtype: string
1052 e5e20779 Faidon Liambotis
    @return: Job ID
1053 e5e20779 Faidon Liambotis

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

1065 e5e20779 Faidon Liambotis
    @type instance: string
1066 e5e20779 Faidon Liambotis
    @param instance: Instance name
1067 e5e20779 Faidon Liambotis
    @type mode: string
1068 e5e20779 Faidon Liambotis
    @param mode: Export mode
1069 e5e20779 Faidon Liambotis
    @rtype: string
1070 e5e20779 Faidon Liambotis
    @return: Job ID
1071 e5e20779 Faidon Liambotis

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

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

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

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

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

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

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

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

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

1179 e5e20779 Faidon Liambotis
    @rtype: list of int
1180 e5e20779 Faidon Liambotis
    @return: job ids for the cluster
1181 e5e20779 Faidon Liambotis

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

1191 a3ad611d Dimitris Aragiorgis
    @type job_id: string
1192 e5e20779 Faidon Liambotis
    @param job_id: job id whose status to query
1193 e5e20779 Faidon Liambotis

1194 e5e20779 Faidon Liambotis
    @rtype: dict
1195 e5e20779 Faidon Liambotis
    @return: job status
1196 e5e20779 Faidon Liambotis

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

1205 a3ad611d Dimitris Aragiorgis
    Completion is defined as any of the following states listed in
1206 a3ad611d Dimitris Aragiorgis
    L{JOB_STATUS_FINALIZED}.
1207 a3ad611d Dimitris Aragiorgis

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

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

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

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

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

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

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

1280 e5e20779 Faidon Liambotis
    @type bulk: bool
1281 e5e20779 Faidon Liambotis
    @param bulk: whether to return all information about all instances
1282 e5e20779 Faidon Liambotis

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

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

1301 e5e20779 Faidon Liambotis
    @type node: str
1302 e5e20779 Faidon Liambotis
    @param node: node whose info to return
1303 e5e20779 Faidon Liambotis

1304 e5e20779 Faidon Liambotis
    @rtype: dict
1305 e5e20779 Faidon Liambotis
    @return: info about the node
1306 e5e20779 Faidon Liambotis

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

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

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

1339 e5e20779 Faidon Liambotis
    @raises GanetiApiError: if an iallocator and remote_node are both
1340 e5e20779 Faidon Liambotis
        specified
1341 e5e20779 Faidon Liambotis

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

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

1395 a3ad611d Dimitris Aragiorgis
    @rtype: string
1396 e5e20779 Faidon Liambotis
    @return: job id
1397 e5e20779 Faidon Liambotis

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

1429 e5e20779 Faidon Liambotis
    @type node: str
1430 e5e20779 Faidon Liambotis
    @param node: node whose role to return
1431 e5e20779 Faidon Liambotis

1432 e5e20779 Faidon Liambotis
    @rtype: str
1433 e5e20779 Faidon Liambotis
    @return: the current role for a node
1434 e5e20779 Faidon Liambotis

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

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

1453 a3ad611d Dimitris Aragiorgis
    @rtype: string
1454 e5e20779 Faidon Liambotis
    @return: job id
1455 e5e20779 Faidon Liambotis

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

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

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

1486 a3ad611d Dimitris Aragiorgis
    More details for parameters can be found in the RAPI documentation.
1487 a3ad611d Dimitris Aragiorgis

1488 a3ad611d Dimitris Aragiorgis
    @type node: string
1489 a3ad611d Dimitris Aragiorgis
    @param node: Node name
1490 a3ad611d Dimitris Aragiorgis
    @rtype: string
1491 a3ad611d Dimitris Aragiorgis
    @return: job id
1492 a3ad611d Dimitris Aragiorgis

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

1501 e5e20779 Faidon Liambotis
    @type node: str
1502 e5e20779 Faidon Liambotis
    @param node: the node whose storage units to return
1503 e5e20779 Faidon Liambotis
    @type storage_type: str
1504 e5e20779 Faidon Liambotis
    @param storage_type: storage type whose units to return
1505 e5e20779 Faidon Liambotis
    @type output_fields: str
1506 e5e20779 Faidon Liambotis
    @param output_fields: storage type fields to return
1507 e5e20779 Faidon Liambotis

1508 a3ad611d Dimitris Aragiorgis
    @rtype: string
1509 e5e20779 Faidon Liambotis
    @return: job id where results can be retrieved
1510 e5e20779 Faidon Liambotis

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

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

1534 a3ad611d Dimitris Aragiorgis
    @rtype: string
1535 e5e20779 Faidon Liambotis
    @return: job id
1536 e5e20779 Faidon Liambotis

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

1552 e5e20779 Faidon Liambotis
    @type node: str
1553 e5e20779 Faidon Liambotis
    @param node: node whose storage units to repair
1554 e5e20779 Faidon Liambotis
    @type storage_type: str
1555 e5e20779 Faidon Liambotis
    @param storage_type: storage type to repair
1556 e5e20779 Faidon Liambotis
    @type name: str
1557 e5e20779 Faidon Liambotis
    @param name: name of the storage unit to repair
1558 e5e20779 Faidon Liambotis

1559 a3ad611d Dimitris Aragiorgis
    @rtype: string
1560 e5e20779 Faidon Liambotis
    @return: job id
1561 e5e20779 Faidon Liambotis

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

1575 e5e20779 Faidon Liambotis
    @type node: str
1576 e5e20779 Faidon Liambotis
    @param node: node whose tags to return
1577 e5e20779 Faidon Liambotis

1578 e5e20779 Faidon Liambotis
    @rtype: list of str
1579 e5e20779 Faidon Liambotis
    @return: tags for the node
1580 e5e20779 Faidon Liambotis

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

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

1596 a3ad611d Dimitris Aragiorgis
    @rtype: string
1597 e5e20779 Faidon Liambotis
    @return: job id
1598 e5e20779 Faidon Liambotis

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

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

1617 a3ad611d Dimitris Aragiorgis
    @rtype: string
1618 e5e20779 Faidon Liambotis
    @return: job id
1619 e5e20779 Faidon Liambotis

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

1631 a3ad611d Dimitris Aragiorgis
    @type bulk: bool
1632 a3ad611d Dimitris Aragiorgis
    @param bulk: whether to return all information about the networks
1633 a3ad611d Dimitris Aragiorgis

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

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

1652 a3ad611d Dimitris Aragiorgis
    @type group: str
1653 a3ad611d Dimitris Aragiorgis
    @param group: name of the network whose info to return
1654 a3ad611d Dimitris Aragiorgis

1655 a3ad611d Dimitris Aragiorgis
    @rtype: dict
1656 a3ad611d Dimitris Aragiorgis
    @return: info about the network
1657 a3ad611d Dimitris Aragiorgis

1658 a3ad611d Dimitris Aragiorgis
    """
1659 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_GET,
1660 a3ad611d Dimitris Aragiorgis
                             "/%s/networks/%s" % (GANETI_RAPI_VERSION, network),
1661 a3ad611d Dimitris Aragiorgis
                             None, None)
1662 a3ad611d Dimitris Aragiorgis
1663 a3ad611d Dimitris Aragiorgis
  def CreateNetwork(self, network_name, network, gateway=None, network6=None,
1664 9ef880a4 Dimitris Aragiorgis
                    gateway6=None, mac_prefix=None, network_type=None,
1665 a406663f Dimitris Aragiorgis
                    add_reserved_ips=None, tags=[],
1666 a406663f Dimitris Aragiorgis
                    conflicts_check=False, dry_run=False):
1667 a3ad611d Dimitris Aragiorgis
    """Creates a new network.
1668 a3ad611d Dimitris Aragiorgis

1669 a3ad611d Dimitris Aragiorgis
    @type name: str
1670 a3ad611d Dimitris Aragiorgis
    @param name: the name of network to create
1671 a3ad611d Dimitris Aragiorgis
    @type dry_run: bool
1672 a3ad611d Dimitris Aragiorgis
    @param dry_run: whether to peform a dry run
1673 a3ad611d Dimitris Aragiorgis

1674 a3ad611d Dimitris Aragiorgis
    @rtype: string
1675 a3ad611d Dimitris Aragiorgis
    @return: job id
1676 a3ad611d Dimitris Aragiorgis

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2004 a3ad611d Dimitris Aragiorgis
    """
2005 a3ad611d Dimitris Aragiorgis
    query = [("tag", t) for t in tags]
2006 a3ad611d Dimitris Aragiorgis
    _AppendDryRunIf(query, dry_run)
2007 a3ad611d Dimitris Aragiorgis
2008 a3ad611d Dimitris Aragiorgis
    return self._SendRequest(HTTP_PUT,
2009 a3ad611d Dimitris Aragiorgis
                             ("/%s/groups/%s/tags" %
2010 a3ad611d Dimitris Aragiorgis
                              (GANETI_RAPI_VERSION, group)), query,