Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / rlib2.py @ 4c98b915

History | View | Annotate | Download (18.5 kB)

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

24 10b207d4 Oleksiy Mishchenko
"""
25 10b207d4 Oleksiy Mishchenko
26 59b4eeef Iustin Pop
from ganeti import opcodes
27 15fd9fd5 Oleksiy Mishchenko
from ganeti import http
28 15fd9fd5 Oleksiy Mishchenko
from ganeti import constants
29 59b4eeef Iustin Pop
from ganeti import cli
30 8381fa2d Michael Hanselmann
from ganeti import rapi
31 38206f3c Iustin Pop
from ganeti.rapi import baserlib
32 10b207d4 Oleksiy Mishchenko
33 4e5a68f8 Oleksiy Mishchenko
34 9031ee8e Iustin Pop
I_FIELDS = ["name", "admin_state", "os",
35 9031ee8e Iustin Pop
            "pnode", "snodes",
36 9031ee8e Iustin Pop
            "disk_template",
37 495cfdf0 Guido Trotter
            "nic.ips", "nic.macs", "nic.modes", "nic.links",
38 a8b16c4e Tim Boring
            "network_port",
39 024e157f Iustin Pop
            "disk.sizes", "disk_usage",
40 a5b9d725 Iustin Pop
            "beparams", "hvparams",
41 9031ee8e Iustin Pop
            "oper_state", "oper_ram", "status",
42 9031ee8e Iustin Pop
            "tags"]
43 9031ee8e Iustin Pop
44 0b2454b9 Iustin Pop
N_FIELDS = ["name", "offline", "master_candidate", "drained",
45 9031ee8e Iustin Pop
            "dtotal", "dfree",
46 4e5a68f8 Oleksiy Mishchenko
            "mtotal", "mnode", "mfree",
47 0105bad3 Iustin Pop
            "pinst_cnt", "sinst_cnt", "tags",
48 0105bad3 Iustin Pop
            "ctotal", "cnodes", "csockets",
49 0105bad3 Iustin Pop
            ]
50 4e5a68f8 Oleksiy Mishchenko
51 64dae8fc Michael Hanselmann
_NR_DRAINED = "drained"
52 64dae8fc Michael Hanselmann
_NR_MASTER_CANDIATE = "master-candidate"
53 64dae8fc Michael Hanselmann
_NR_MASTER = "master"
54 64dae8fc Michael Hanselmann
_NR_OFFLINE = "offline"
55 64dae8fc Michael Hanselmann
_NR_REGULAR = "regular"
56 64dae8fc Michael Hanselmann
57 64dae8fc Michael Hanselmann
_NR_MAP = {
58 64dae8fc Michael Hanselmann
  "M": _NR_MASTER,
59 64dae8fc Michael Hanselmann
  "C": _NR_MASTER_CANDIATE,
60 64dae8fc Michael Hanselmann
  "D": _NR_DRAINED,
61 64dae8fc Michael Hanselmann
  "O": _NR_OFFLINE,
62 64dae8fc Michael Hanselmann
  "R": _NR_REGULAR,
63 64dae8fc Michael Hanselmann
  }
64 64dae8fc Michael Hanselmann
65 4e5a68f8 Oleksiy Mishchenko
66 4e5a68f8 Oleksiy Mishchenko
class R_version(baserlib.R_Generic):
67 4e5a68f8 Oleksiy Mishchenko
  """/version resource.
68 4e5a68f8 Oleksiy Mishchenko

69 4e5a68f8 Oleksiy Mishchenko
  This resource should be used to determine the remote API version and
70 4e5a68f8 Oleksiy Mishchenko
  to adapt clients accordingly.
71 4e5a68f8 Oleksiy Mishchenko

72 4e5a68f8 Oleksiy Mishchenko
  """
73 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
74 4e5a68f8 Oleksiy Mishchenko
    """Returns the remote API version.
75 4e5a68f8 Oleksiy Mishchenko

76 4e5a68f8 Oleksiy Mishchenko
    """
77 4e5a68f8 Oleksiy Mishchenko
    return constants.RAPI_VERSION
78 4e5a68f8 Oleksiy Mishchenko
79 4e5a68f8 Oleksiy Mishchenko
80 4e5a68f8 Oleksiy Mishchenko
class R_2_info(baserlib.R_Generic):
81 4e5a68f8 Oleksiy Mishchenko
  """Cluster info.
82 4e5a68f8 Oleksiy Mishchenko

83 4e5a68f8 Oleksiy Mishchenko
  """
84 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
85 4e5a68f8 Oleksiy Mishchenko
    """Returns cluster information.
86 4e5a68f8 Oleksiy Mishchenko

87 4e5a68f8 Oleksiy Mishchenko
    """
88 59b4eeef Iustin Pop
    client = baserlib.GetClient()
89 9031ee8e Iustin Pop
    return client.QueryClusterInfo()
90 4e5a68f8 Oleksiy Mishchenko
91 4e5a68f8 Oleksiy Mishchenko
92 4e5a68f8 Oleksiy Mishchenko
class R_2_os(baserlib.R_Generic):
93 4e5a68f8 Oleksiy Mishchenko
  """/2/os resource.
94 4e5a68f8 Oleksiy Mishchenko

95 4e5a68f8 Oleksiy Mishchenko
  """
96 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
97 4e5a68f8 Oleksiy Mishchenko
    """Return a list of all OSes.
98 4e5a68f8 Oleksiy Mishchenko

99 4e5a68f8 Oleksiy Mishchenko
    Can return error 500 in case of a problem.
100 4e5a68f8 Oleksiy Mishchenko

101 4e5a68f8 Oleksiy Mishchenko
    Example: ["debian-etch"]
102 4e5a68f8 Oleksiy Mishchenko

103 4e5a68f8 Oleksiy Mishchenko
    """
104 59b4eeef Iustin Pop
    cl = baserlib.GetClient()
105 59b4eeef Iustin Pop
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
106 59b4eeef Iustin Pop
    job_id = baserlib.SubmitJob([op], cl)
107 59b4eeef Iustin Pop
    # we use custom feedback function, instead of print we log the status
108 59b4eeef Iustin Pop
    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
109 59b4eeef Iustin Pop
    diagnose_data = result[0]
110 4e5a68f8 Oleksiy Mishchenko
111 4e5a68f8 Oleksiy Mishchenko
    if not isinstance(diagnose_data, list):
112 59b4eeef Iustin Pop
      raise http.HttpBadGateway(message="Can't get OS list")
113 4e5a68f8 Oleksiy Mishchenko
114 4e5a68f8 Oleksiy Mishchenko
    return [row[0] for row in diagnose_data if row[1]]
115 51ee2f49 Oleksiy Mishchenko
116 10b207d4 Oleksiy Mishchenko
117 10b207d4 Oleksiy Mishchenko
class R_2_jobs(baserlib.R_Generic):
118 10b207d4 Oleksiy Mishchenko
  """/2/jobs resource.
119 10b207d4 Oleksiy Mishchenko

120 10b207d4 Oleksiy Mishchenko
  """
121 10b207d4 Oleksiy Mishchenko
  def GET(self):
122 10b207d4 Oleksiy Mishchenko
    """Returns a dictionary of jobs.
123 10b207d4 Oleksiy Mishchenko

124 c41eea6e Iustin Pop
    @return: a dictionary with jobs id and uri.
125 38206f3c Iustin Pop

126 10b207d4 Oleksiy Mishchenko
    """
127 10b207d4 Oleksiy Mishchenko
    fields = ["id"]
128 59b4eeef Iustin Pop
    cl = baserlib.GetClient()
129 10b207d4 Oleksiy Mishchenko
    # Convert the list of lists to the list of ids
130 59b4eeef Iustin Pop
    result = [job_id for [job_id] in cl.QueryJobs(None, fields)]
131 9031ee8e Iustin Pop
    return baserlib.BuildUriList(result, "/2/jobs/%s",
132 9031ee8e Iustin Pop
                                 uri_fields=("id", "uri"))
133 10b207d4 Oleksiy Mishchenko
134 10b207d4 Oleksiy Mishchenko
135 10b207d4 Oleksiy Mishchenko
class R_2_jobs_id(baserlib.R_Generic):
136 10b207d4 Oleksiy Mishchenko
  """/2/jobs/[job_id] resource.
137 10b207d4 Oleksiy Mishchenko

138 10b207d4 Oleksiy Mishchenko
  """
139 10b207d4 Oleksiy Mishchenko
  def GET(self):
140 10b207d4 Oleksiy Mishchenko
    """Returns a job status.
141 10b207d4 Oleksiy Mishchenko

142 c41eea6e Iustin Pop
    @return: a dictionary with job parameters.
143 c41eea6e Iustin Pop
        The result includes:
144 c41eea6e Iustin Pop
            - id: job ID as a number
145 c41eea6e Iustin Pop
            - status: current job status as a string
146 c41eea6e Iustin Pop
            - ops: involved OpCodes as a list of dictionaries for each
147 c41eea6e Iustin Pop
              opcodes in the job
148 c41eea6e Iustin Pop
            - opstatus: OpCodes status as a list
149 c41eea6e Iustin Pop
            - opresult: OpCodes results as a list of lists
150 38206f3c Iustin Pop

151 10b207d4 Oleksiy Mishchenko
    """
152 ee69c97f Iustin Pop
    fields = ["id", "ops", "status", "summary",
153 ee69c97f Iustin Pop
              "opstatus", "opresult", "oplog",
154 ee69c97f Iustin Pop
              "received_ts", "start_ts", "end_ts",
155 ee69c97f Iustin Pop
              ]
156 10b207d4 Oleksiy Mishchenko
    job_id = self.items[0]
157 59b4eeef Iustin Pop
    result = baserlib.GetClient().QueryJobs([job_id, ], fields)[0]
158 ee69c97f Iustin Pop
    if result is None:
159 ee69c97f Iustin Pop
      raise http.HttpNotFound()
160 10b207d4 Oleksiy Mishchenko
    return baserlib.MapFields(fields, result)
161 10b207d4 Oleksiy Mishchenko
162 c7f5f338 Oleksiy Mishchenko
  def DELETE(self):
163 c7f5f338 Oleksiy Mishchenko
    """Cancel not-yet-started job.
164 c7f5f338 Oleksiy Mishchenko

165 c7f5f338 Oleksiy Mishchenko
    """
166 c7f5f338 Oleksiy Mishchenko
    job_id = self.items[0]
167 59b4eeef Iustin Pop
    result = baserlib.GetClient().CancelJob(job_id)
168 c7f5f338 Oleksiy Mishchenko
    return result
169 c7f5f338 Oleksiy Mishchenko
170 10b207d4 Oleksiy Mishchenko
171 10b207d4 Oleksiy Mishchenko
class R_2_nodes(baserlib.R_Generic):
172 10b207d4 Oleksiy Mishchenko
  """/2/nodes resource.
173 10b207d4 Oleksiy Mishchenko

174 10b207d4 Oleksiy Mishchenko
  """
175 10b207d4 Oleksiy Mishchenko
  def GET(self):
176 10b207d4 Oleksiy Mishchenko
    """Returns a list of all nodes.
177 38206f3c Iustin Pop

178 10b207d4 Oleksiy Mishchenko
    """
179 59b4eeef Iustin Pop
    client = baserlib.GetClient()
180 38206f3c Iustin Pop
181 3d103742 Iustin Pop
    if self.useBulk():
182 9031ee8e Iustin Pop
      bulkdata = client.QueryNodes([], N_FIELDS, False)
183 a0dcf7c2 Oleksiy Mishchenko
      return baserlib.MapBulkFields(bulkdata, N_FIELDS)
184 9031ee8e Iustin Pop
    else:
185 9031ee8e Iustin Pop
      nodesdata = client.QueryNodes([], ["name"], False)
186 9031ee8e Iustin Pop
      nodeslist = [row[0] for row in nodesdata]
187 9031ee8e Iustin Pop
      return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
188 9031ee8e Iustin Pop
                                   uri_fields=("id", "uri"))
189 441e7cfd Oleksiy Mishchenko
190 441e7cfd Oleksiy Mishchenko
191 4e5a68f8 Oleksiy Mishchenko
class R_2_nodes_name(baserlib.R_Generic):
192 4e5a68f8 Oleksiy Mishchenko
  """/2/nodes/[node_name] resources.
193 028c6b76 Oleksiy Mishchenko

194 028c6b76 Oleksiy Mishchenko
  """
195 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
196 4e5a68f8 Oleksiy Mishchenko
    """Send information about a node.
197 4e5a68f8 Oleksiy Mishchenko

198 4e5a68f8 Oleksiy Mishchenko
    """
199 4e5a68f8 Oleksiy Mishchenko
    node_name = self.items[0]
200 59b4eeef Iustin Pop
    client = baserlib.GetClient()
201 9031ee8e Iustin Pop
    result = client.QueryNodes(names=[node_name], fields=N_FIELDS,
202 3d103742 Iustin Pop
                               use_locking=self.useLocking())
203 4e5a68f8 Oleksiy Mishchenko
204 4e5a68f8 Oleksiy Mishchenko
    return baserlib.MapFields(N_FIELDS, result[0])
205 028c6b76 Oleksiy Mishchenko
206 028c6b76 Oleksiy Mishchenko
207 64dae8fc Michael Hanselmann
class R_2_nodes_name_role(baserlib.R_Generic):
208 64dae8fc Michael Hanselmann
  """ /2/nodes/[node_name]/role resource.
209 64dae8fc Michael Hanselmann

210 64dae8fc Michael Hanselmann
  """
211 64dae8fc Michael Hanselmann
  def GET(self):
212 64dae8fc Michael Hanselmann
    """Returns the current node role.
213 64dae8fc Michael Hanselmann

214 64dae8fc Michael Hanselmann
    @return: Node role
215 64dae8fc Michael Hanselmann

216 64dae8fc Michael Hanselmann
    """
217 64dae8fc Michael Hanselmann
    node_name = self.items[0]
218 64dae8fc Michael Hanselmann
    client = baserlib.GetClient()
219 64dae8fc Michael Hanselmann
    result = client.QueryNodes(names=[node_name], fields=["role"],
220 64dae8fc Michael Hanselmann
                               use_locking=self.useLocking())
221 64dae8fc Michael Hanselmann
222 64dae8fc Michael Hanselmann
    return _NR_MAP[result[0][0]]
223 64dae8fc Michael Hanselmann
224 64dae8fc Michael Hanselmann
  def PUT(self):
225 64dae8fc Michael Hanselmann
    """Sets the node role.
226 64dae8fc Michael Hanselmann

227 64dae8fc Michael Hanselmann
    @return: a job id
228 64dae8fc Michael Hanselmann

229 64dae8fc Michael Hanselmann
    """
230 64dae8fc Michael Hanselmann
    if not isinstance(self.req.request_body, basestring):
231 64dae8fc Michael Hanselmann
      raise http.HttpBadRequest("Invalid body contents, not a string")
232 64dae8fc Michael Hanselmann
233 64dae8fc Michael Hanselmann
    node_name = self.items[0]
234 64dae8fc Michael Hanselmann
    role = self.req.request_body
235 64dae8fc Michael Hanselmann
236 64dae8fc Michael Hanselmann
    if role == _NR_REGULAR:
237 64dae8fc Michael Hanselmann
      candidate = False
238 64dae8fc Michael Hanselmann
      offline = False
239 64dae8fc Michael Hanselmann
      drained = False
240 64dae8fc Michael Hanselmann
241 64dae8fc Michael Hanselmann
    elif role == _NR_MASTER_CANDIATE:
242 64dae8fc Michael Hanselmann
      candidate = True
243 64dae8fc Michael Hanselmann
      offline = drained = None
244 64dae8fc Michael Hanselmann
245 64dae8fc Michael Hanselmann
    elif role == _NR_DRAINED:
246 64dae8fc Michael Hanselmann
      drained = True
247 64dae8fc Michael Hanselmann
      candidate = offline = None
248 64dae8fc Michael Hanselmann
249 64dae8fc Michael Hanselmann
    elif role == _NR_OFFLINE:
250 64dae8fc Michael Hanselmann
      offline = True
251 64dae8fc Michael Hanselmann
      candidate = drained = None
252 64dae8fc Michael Hanselmann
253 64dae8fc Michael Hanselmann
    else:
254 64dae8fc Michael Hanselmann
      raise http.HttpBadRequest("Can't set '%s' role" % role)
255 64dae8fc Michael Hanselmann
256 64dae8fc Michael Hanselmann
    op = opcodes.OpSetNodeParams(node_name=node_name,
257 64dae8fc Michael Hanselmann
                                 master_candidate=candidate,
258 64dae8fc Michael Hanselmann
                                 offline=offline,
259 64dae8fc Michael Hanselmann
                                 drained=drained,
260 64dae8fc Michael Hanselmann
                                 force=bool(self.useForce()))
261 64dae8fc Michael Hanselmann
262 64dae8fc Michael Hanselmann
    return baserlib.SubmitJob([op])
263 64dae8fc Michael Hanselmann
264 64dae8fc Michael Hanselmann
265 73452f12 Michael Hanselmann
class R_2_nodes_name_evacuate(baserlib.R_Generic):
266 73452f12 Michael Hanselmann
  """/2/nodes/[node_name]/evacuate resource.
267 73452f12 Michael Hanselmann

268 73452f12 Michael Hanselmann
  """
269 73452f12 Michael Hanselmann
  def POST(self):
270 73452f12 Michael Hanselmann
    """Evacuate all secondary instances off a node.
271 73452f12 Michael Hanselmann

272 73452f12 Michael Hanselmann
    """
273 73452f12 Michael Hanselmann
    node_name = self.items[0]
274 73452f12 Michael Hanselmann
    remote_node = self._checkStringVariable("remote_node", default=None)
275 73452f12 Michael Hanselmann
    iallocator = self._checkStringVariable("iallocator", default=None)
276 73452f12 Michael Hanselmann
277 73452f12 Michael Hanselmann
    op = opcodes.OpEvacuateNode(node_name=node_name,
278 73452f12 Michael Hanselmann
                                remote_node=remote_node,
279 73452f12 Michael Hanselmann
                                iallocator=iallocator)
280 73452f12 Michael Hanselmann
281 73452f12 Michael Hanselmann
    return baserlib.SubmitJob([op])
282 73452f12 Michael Hanselmann
283 73452f12 Michael Hanselmann
284 1c482bab Michael Hanselmann
class R_2_nodes_name_migrate(baserlib.R_Generic):
285 7a95a954 Michael Hanselmann
  """/2/nodes/[node_name]/migrate resource.
286 1c482bab Michael Hanselmann

287 1c482bab Michael Hanselmann
  """
288 1c482bab Michael Hanselmann
  def POST(self):
289 1c482bab Michael Hanselmann
    """Migrate all primary instances from a node.
290 1c482bab Michael Hanselmann

291 1c482bab Michael Hanselmann
    """
292 1c482bab Michael Hanselmann
    node_name = self.items[0]
293 1c482bab Michael Hanselmann
    live = bool(self._checkIntVariable("live", default=1))
294 1c482bab Michael Hanselmann
295 1c482bab Michael Hanselmann
    op = opcodes.OpMigrateNode(node_name=node_name, live=live)
296 1c482bab Michael Hanselmann
297 1c482bab Michael Hanselmann
    return baserlib.SubmitJob([op])
298 1c482bab Michael Hanselmann
299 1c482bab Michael Hanselmann
300 7a95a954 Michael Hanselmann
class R_2_nodes_name_storage(baserlib.R_Generic):
301 7a95a954 Michael Hanselmann
  """/2/nodes/[node_name]/storage ressource.
302 7a95a954 Michael Hanselmann

303 7a95a954 Michael Hanselmann
  """
304 7a95a954 Michael Hanselmann
  # LUQueryNodeStorage acquires locks, hence restricting access to GET
305 7a95a954 Michael Hanselmann
  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
306 7a95a954 Michael Hanselmann
307 7a95a954 Michael Hanselmann
  def GET(self):
308 7a95a954 Michael Hanselmann
    node_name = self.items[0]
309 7a95a954 Michael Hanselmann
310 7a95a954 Michael Hanselmann
    storage_type = self._checkStringVariable("storage_type", None)
311 7a95a954 Michael Hanselmann
    if not storage_type:
312 7a95a954 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'storage_type'"
313 7a95a954 Michael Hanselmann
                                " parameter")
314 7a95a954 Michael Hanselmann
315 7a95a954 Michael Hanselmann
    output_fields = self._checkStringVariable("output_fields", None)
316 7a95a954 Michael Hanselmann
    if not output_fields:
317 7a95a954 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'output_fields'"
318 7a95a954 Michael Hanselmann
                                " parameter")
319 7a95a954 Michael Hanselmann
320 7a95a954 Michael Hanselmann
    op = opcodes.OpQueryNodeStorage(nodes=[node_name],
321 7a95a954 Michael Hanselmann
                                    storage_type=storage_type,
322 7a95a954 Michael Hanselmann
                                    output_fields=output_fields.split(","))
323 7a95a954 Michael Hanselmann
    return baserlib.SubmitJob([op])
324 7a95a954 Michael Hanselmann
325 7a95a954 Michael Hanselmann
326 1e82bc80 Michael Hanselmann
class R_2_nodes_name_storage_modify(baserlib.R_Generic):
327 1e82bc80 Michael Hanselmann
  """/2/nodes/[node_name]/storage/modify ressource.
328 1e82bc80 Michael Hanselmann

329 1e82bc80 Michael Hanselmann
  """
330 1e82bc80 Michael Hanselmann
  def PUT(self):
331 1e82bc80 Michael Hanselmann
    node_name = self.items[0]
332 1e82bc80 Michael Hanselmann
333 1e82bc80 Michael Hanselmann
    storage_type = self._checkStringVariable("storage_type", None)
334 1e82bc80 Michael Hanselmann
    if not storage_type:
335 1e82bc80 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'storage_type'"
336 1e82bc80 Michael Hanselmann
                                " parameter")
337 1e82bc80 Michael Hanselmann
338 1e82bc80 Michael Hanselmann
    name = self._checkStringVariable("name", None)
339 1e82bc80 Michael Hanselmann
    if not name:
340 1e82bc80 Michael Hanselmann
      raise http.HttpBadRequest("Missing the required 'name'"
341 1e82bc80 Michael Hanselmann
                                " parameter")
342 1e82bc80 Michael Hanselmann
343 1e82bc80 Michael Hanselmann
    changes = {}
344 1e82bc80 Michael Hanselmann
345 1e82bc80 Michael Hanselmann
    if "allocatable" in self.queryargs:
346 1e82bc80 Michael Hanselmann
      changes[constants.SF_ALLOCATABLE] = \
347 1e82bc80 Michael Hanselmann
        bool(self._checkIntVariable("allocatable", default=1))
348 1e82bc80 Michael Hanselmann
349 1e82bc80 Michael Hanselmann
    op = opcodes.OpModifyNodeStorage(node_name=node_name,
350 1e82bc80 Michael Hanselmann
                                     storage_type=storage_type,
351 1e82bc80 Michael Hanselmann
                                     name=name,
352 1e82bc80 Michael Hanselmann
                                     changes=changes)
353 1e82bc80 Michael Hanselmann
    return baserlib.SubmitJob([op])
354 1e82bc80 Michael Hanselmann
355 1e82bc80 Michael Hanselmann
356 441e7cfd Oleksiy Mishchenko
class R_2_instances(baserlib.R_Generic):
357 441e7cfd Oleksiy Mishchenko
  """/2/instances resource.
358 441e7cfd Oleksiy Mishchenko

359 441e7cfd Oleksiy Mishchenko
  """
360 441e7cfd Oleksiy Mishchenko
  def GET(self):
361 441e7cfd Oleksiy Mishchenko
    """Returns a list of all available instances.
362 441e7cfd Oleksiy Mishchenko

363 441e7cfd Oleksiy Mishchenko
    """
364 59b4eeef Iustin Pop
    client = baserlib.GetClient()
365 441e7cfd Oleksiy Mishchenko
366 3d103742 Iustin Pop
    use_locking = self.useLocking()
367 3d103742 Iustin Pop
    if self.useBulk():
368 3d103742 Iustin Pop
      bulkdata = client.QueryInstances([], I_FIELDS, use_locking)
369 a0dcf7c2 Oleksiy Mishchenko
      return baserlib.MapBulkFields(bulkdata, I_FIELDS)
370 441e7cfd Oleksiy Mishchenko
    else:
371 3d103742 Iustin Pop
      instancesdata = client.QueryInstances([], ["name"], use_locking)
372 9031ee8e Iustin Pop
      instanceslist = [row[0] for row in instancesdata]
373 441e7cfd Oleksiy Mishchenko
      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
374 441e7cfd Oleksiy Mishchenko
                                   uri_fields=("id", "uri"))
375 441e7cfd Oleksiy Mishchenko
376 21f04e5e Oleksiy Mishchenko
  def POST(self):
377 2f7635f4 Oleksiy Mishchenko
    """Create an instance.
378 2f7635f4 Oleksiy Mishchenko

379 5fcc718f Iustin Pop
    @return: a job id
380 2f7635f4 Oleksiy Mishchenko

381 2f7635f4 Oleksiy Mishchenko
    """
382 6e99c5a0 Iustin Pop
    if not isinstance(self.req.request_body, dict):
383 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Invalid body contents, not a dictionary")
384 6e99c5a0 Iustin Pop
385 6e99c5a0 Iustin Pop
    beparams = baserlib.MakeParamsDict(self.req.request_body,
386 6e99c5a0 Iustin Pop
                                       constants.BES_PARAMETERS)
387 6e99c5a0 Iustin Pop
    hvparams = baserlib.MakeParamsDict(self.req.request_body,
388 6e99c5a0 Iustin Pop
                                       constants.HVS_PARAMETERS)
389 6e99c5a0 Iustin Pop
    fn = self.getBodyParameter
390 6e99c5a0 Iustin Pop
391 6e99c5a0 Iustin Pop
    # disk processing
392 6e99c5a0 Iustin Pop
    disk_data = fn('disks')
393 6e99c5a0 Iustin Pop
    if not isinstance(disk_data, list):
394 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("The 'disks' parameter should be a list")
395 6e99c5a0 Iustin Pop
    disks = []
396 6e99c5a0 Iustin Pop
    for idx, d in enumerate(disk_data):
397 6e99c5a0 Iustin Pop
      if not isinstance(d, int):
398 6e99c5a0 Iustin Pop
        raise http.HttpBadRequest("Disk %d specification wrong: should"
399 6e99c5a0 Iustin Pop
                                  " be an integer")
400 6e99c5a0 Iustin Pop
      disks.append({"size": d})
401 6e99c5a0 Iustin Pop
    # nic processing (one nic only)
402 495cfdf0 Guido Trotter
    nics = [{"mac": fn("mac", constants.VALUE_AUTO)}]
403 495cfdf0 Guido Trotter
    if fn("ip", None) is not None:
404 495cfdf0 Guido Trotter
      nics[0]["ip"] = fn("ip")
405 495cfdf0 Guido Trotter
    if fn("mode", None) is not None:
406 495cfdf0 Guido Trotter
      nics[0]["mode"] = fn("mode")
407 495cfdf0 Guido Trotter
    if fn("link", None) is not None:
408 495cfdf0 Guido Trotter
      nics[0]["link"] = fn("link")
409 495cfdf0 Guido Trotter
    if fn("bridge", None) is not None:
410 495cfdf0 Guido Trotter
       nics[0]["bridge"] = fn("bridge")
411 2f7635f4 Oleksiy Mishchenko
412 59b4eeef Iustin Pop
    op = opcodes.OpCreateInstance(
413 59b4eeef Iustin Pop
      mode=constants.INSTANCE_CREATE,
414 59b4eeef Iustin Pop
      instance_name=fn('name'),
415 59b4eeef Iustin Pop
      disks=disks,
416 59b4eeef Iustin Pop
      disk_template=fn('disk_template'),
417 59b4eeef Iustin Pop
      os_type=fn('os'),
418 59b4eeef Iustin Pop
      pnode=fn('pnode', None),
419 59b4eeef Iustin Pop
      snode=fn('snode', None),
420 59b4eeef Iustin Pop
      iallocator=fn('iallocator', None),
421 59b4eeef Iustin Pop
      nics=nics,
422 59b4eeef Iustin Pop
      start=fn('start', True),
423 59b4eeef Iustin Pop
      ip_check=fn('ip_check', True),
424 59b4eeef Iustin Pop
      wait_for_sync=True,
425 59b4eeef Iustin Pop
      hypervisor=fn('hypervisor', None),
426 59b4eeef Iustin Pop
      hvparams=hvparams,
427 59b4eeef Iustin Pop
      beparams=beparams,
428 59b4eeef Iustin Pop
      file_storage_dir=fn('file_storage_dir', None),
429 59b4eeef Iustin Pop
      file_driver=fn('file_driver', 'loop'),
430 6f59b964 Iustin Pop
      dry_run=bool(self.dryRun()),
431 59b4eeef Iustin Pop
      )
432 59b4eeef Iustin Pop
433 59b4eeef Iustin Pop
    return baserlib.SubmitJob([op])
434 2f7635f4 Oleksiy Mishchenko
435 441e7cfd Oleksiy Mishchenko
436 4e5a68f8 Oleksiy Mishchenko
class R_2_instances_name(baserlib.R_Generic):
437 4e5a68f8 Oleksiy Mishchenko
  """/2/instances/[instance_name] resources.
438 028c6b76 Oleksiy Mishchenko

439 028c6b76 Oleksiy Mishchenko
  """
440 4e5a68f8 Oleksiy Mishchenko
  def GET(self):
441 4e5a68f8 Oleksiy Mishchenko
    """Send information about an instance.
442 4e5a68f8 Oleksiy Mishchenko

443 4e5a68f8 Oleksiy Mishchenko
    """
444 59b4eeef Iustin Pop
    client = baserlib.GetClient()
445 4e5a68f8 Oleksiy Mishchenko
    instance_name = self.items[0]
446 9031ee8e Iustin Pop
    result = client.QueryInstances(names=[instance_name], fields=I_FIELDS,
447 3d103742 Iustin Pop
                                   use_locking=self.useLocking())
448 4e5a68f8 Oleksiy Mishchenko
449 4e5a68f8 Oleksiy Mishchenko
    return baserlib.MapFields(I_FIELDS, result[0])
450 028c6b76 Oleksiy Mishchenko
451 6e99c5a0 Iustin Pop
  def DELETE(self):
452 6e99c5a0 Iustin Pop
    """Delete an instance.
453 6e99c5a0 Iustin Pop

454 6e99c5a0 Iustin Pop
    """
455 59b4eeef Iustin Pop
    op = opcodes.OpRemoveInstance(instance_name=self.items[0],
456 6f59b964 Iustin Pop
                                  ignore_failures=False,
457 6f59b964 Iustin Pop
                                  dry_run=bool(self.dryRun()))
458 59b4eeef Iustin Pop
    return baserlib.SubmitJob([op])
459 6e99c5a0 Iustin Pop
460 028c6b76 Oleksiy Mishchenko
461 2276aa29 Oleksiy Mishchenko
class R_2_instances_name_reboot(baserlib.R_Generic):
462 2276aa29 Oleksiy Mishchenko
  """/2/instances/[instance_name]/reboot resource.
463 2276aa29 Oleksiy Mishchenko

464 2276aa29 Oleksiy Mishchenko
  Implements an instance reboot.
465 2276aa29 Oleksiy Mishchenko

466 2276aa29 Oleksiy Mishchenko
  """
467 21f04e5e Oleksiy Mishchenko
  def POST(self):
468 2276aa29 Oleksiy Mishchenko
    """Reboot an instance.
469 2276aa29 Oleksiy Mishchenko

470 0c55c24b Oleksiy Mishchenko
    The URI takes type=[hard|soft|full] and
471 0c55c24b Oleksiy Mishchenko
    ignore_secondaries=[False|True] parameters.
472 0c55c24b Oleksiy Mishchenko

473 2276aa29 Oleksiy Mishchenko
    """
474 2276aa29 Oleksiy Mishchenko
    instance_name = self.items[0]
475 0c55c24b Oleksiy Mishchenko
    reboot_type = self.queryargs.get('type',
476 0c55c24b Oleksiy Mishchenko
                                     [constants.INSTANCE_REBOOT_HARD])[0]
477 0c55c24b Oleksiy Mishchenko
    ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
478 0c55c24b Oleksiy Mishchenko
                                                 [False])[0])
479 59b4eeef Iustin Pop
    op = opcodes.OpRebootInstance(instance_name=instance_name,
480 59b4eeef Iustin Pop
                                  reboot_type=reboot_type,
481 6f59b964 Iustin Pop
                                  ignore_secondaries=ignore_secondaries,
482 6f59b964 Iustin Pop
                                  dry_run=bool(self.dryRun()))
483 2276aa29 Oleksiy Mishchenko
484 59b4eeef Iustin Pop
    return baserlib.SubmitJob([op])
485 2276aa29 Oleksiy Mishchenko
486 2276aa29 Oleksiy Mishchenko
487 0c55c24b Oleksiy Mishchenko
class R_2_instances_name_startup(baserlib.R_Generic):
488 0c55c24b Oleksiy Mishchenko
  """/2/instances/[instance_name]/startup resource.
489 0c55c24b Oleksiy Mishchenko

490 0c55c24b Oleksiy Mishchenko
  Implements an instance startup.
491 0c55c24b Oleksiy Mishchenko

492 0c55c24b Oleksiy Mishchenko
  """
493 21f04e5e Oleksiy Mishchenko
  def PUT(self):
494 0c55c24b Oleksiy Mishchenko
    """Startup an instance.
495 0c55c24b Oleksiy Mishchenko

496 c41eea6e Iustin Pop
    The URI takes force=[False|True] parameter to start the instance
497 c41eea6e Iustin Pop
    if even if secondary disks are failing.
498 0c55c24b Oleksiy Mishchenko

499 0c55c24b Oleksiy Mishchenko
    """
500 0c55c24b Oleksiy Mishchenko
    instance_name = self.items[0]
501 0c55c24b Oleksiy Mishchenko
    force_startup = bool(self.queryargs.get('force', [False])[0])
502 59b4eeef Iustin Pop
    op = opcodes.OpStartupInstance(instance_name=instance_name,
503 6f59b964 Iustin Pop
                                   force=force_startup,
504 6f59b964 Iustin Pop
                                   dry_run=bool(self.dryRun()))
505 0c55c24b Oleksiy Mishchenko
506 59b4eeef Iustin Pop
    return baserlib.SubmitJob([op])
507 0c55c24b Oleksiy Mishchenko
508 0c55c24b Oleksiy Mishchenko
509 0c55c24b Oleksiy Mishchenko
class R_2_instances_name_shutdown(baserlib.R_Generic):
510 0c55c24b Oleksiy Mishchenko
  """/2/instances/[instance_name]/shutdown resource.
511 0c55c24b Oleksiy Mishchenko

512 0c55c24b Oleksiy Mishchenko
  Implements an instance shutdown.
513 0c55c24b Oleksiy Mishchenko

514 0c55c24b Oleksiy Mishchenko
  """
515 21f04e5e Oleksiy Mishchenko
  def PUT(self):
516 0c55c24b Oleksiy Mishchenko
    """Shutdown an instance.
517 0c55c24b Oleksiy Mishchenko

518 0c55c24b Oleksiy Mishchenko
    """
519 0c55c24b Oleksiy Mishchenko
    instance_name = self.items[0]
520 6f59b964 Iustin Pop
    op = opcodes.OpShutdownInstance(instance_name=instance_name,
521 6f59b964 Iustin Pop
                                    dry_run=bool(self.dryRun()))
522 0c55c24b Oleksiy Mishchenko
523 59b4eeef Iustin Pop
    return baserlib.SubmitJob([op])
524 0c55c24b Oleksiy Mishchenko
525 0c55c24b Oleksiy Mishchenko
526 e5b7c4ca Iustin Pop
class R_2_instances_name_reinstall(baserlib.R_Generic):
527 e5b7c4ca Iustin Pop
  """/2/instances/[instance_name]/reinstall resource.
528 e5b7c4ca Iustin Pop

529 e5b7c4ca Iustin Pop
  Implements an instance reinstall.
530 e5b7c4ca Iustin Pop

531 e5b7c4ca Iustin Pop
  """
532 e5b7c4ca Iustin Pop
  def POST(self):
533 e5b7c4ca Iustin Pop
    """Reinstall an instance.
534 e5b7c4ca Iustin Pop

535 e5b7c4ca Iustin Pop
    The URI takes os=name and nostartup=[0|1] optional
536 e5b7c4ca Iustin Pop
    parameters. By default, the instance will be started
537 e5b7c4ca Iustin Pop
    automatically.
538 e5b7c4ca Iustin Pop

539 e5b7c4ca Iustin Pop
    """
540 e5b7c4ca Iustin Pop
    instance_name = self.items[0]
541 e5b7c4ca Iustin Pop
    ostype = self._checkStringVariable('os')
542 e5b7c4ca Iustin Pop
    nostartup = self._checkIntVariable('nostartup')
543 e5b7c4ca Iustin Pop
    ops = [
544 e5b7c4ca Iustin Pop
      opcodes.OpShutdownInstance(instance_name=instance_name),
545 e5b7c4ca Iustin Pop
      opcodes.OpReinstallInstance(instance_name=instance_name, os_type=ostype),
546 e5b7c4ca Iustin Pop
      ]
547 e5b7c4ca Iustin Pop
    if not nostartup:
548 e5b7c4ca Iustin Pop
      ops.append(opcodes.OpStartupInstance(instance_name=instance_name,
549 e5b7c4ca Iustin Pop
                                           force=False))
550 e5b7c4ca Iustin Pop
    return baserlib.SubmitJob(ops)
551 e5b7c4ca Iustin Pop
552 e5b7c4ca Iustin Pop
553 4c98b915 Michael Hanselmann
class R_2_instances_name_replace_disks(baserlib.R_Generic):
554 4c98b915 Michael Hanselmann
  """/2/instances/[instance_name]/replace-disks resource.
555 4c98b915 Michael Hanselmann

556 4c98b915 Michael Hanselmann
  """
557 4c98b915 Michael Hanselmann
  def POST(self):
558 4c98b915 Michael Hanselmann
    """Replaces disks on an instance.
559 4c98b915 Michael Hanselmann

560 4c98b915 Michael Hanselmann
    """
561 4c98b915 Michael Hanselmann
    instance_name = self.items[0]
562 4c98b915 Michael Hanselmann
    remote_node = self._checkStringVariable("remote_node", default=None)
563 4c98b915 Michael Hanselmann
    mode = self._checkStringVariable("mode", default=None)
564 4c98b915 Michael Hanselmann
    raw_disks = self._checkStringVariable("disks", default=None)
565 4c98b915 Michael Hanselmann
    iallocator = self._checkStringVariable("iallocator", default=None)
566 4c98b915 Michael Hanselmann
567 4c98b915 Michael Hanselmann
    if raw_disks:
568 4c98b915 Michael Hanselmann
      try:
569 4c98b915 Michael Hanselmann
        disks = [int(part) for part in raw_disks.split(",")]
570 4c98b915 Michael Hanselmann
      except ValueError, err:
571 4c98b915 Michael Hanselmann
        raise http.HttpBadRequest("Invalid disk index passed: %s" % str(err))
572 4c98b915 Michael Hanselmann
    else:
573 4c98b915 Michael Hanselmann
      disks = []
574 4c98b915 Michael Hanselmann
575 4c98b915 Michael Hanselmann
    op = opcodes.OpReplaceDisks(instance_name=instance_name,
576 4c98b915 Michael Hanselmann
                                remote_node=remote_node,
577 4c98b915 Michael Hanselmann
                                mode=mode,
578 4c98b915 Michael Hanselmann
                                disks=disks,
579 4c98b915 Michael Hanselmann
                                iallocator=iallocator)
580 4c98b915 Michael Hanselmann
581 4c98b915 Michael Hanselmann
    return baserlib.SubmitJob([op])
582 4c98b915 Michael Hanselmann
583 4c98b915 Michael Hanselmann
584 18cb43a2 Oleksiy Mishchenko
class _R_Tags(baserlib.R_Generic):
585 18cb43a2 Oleksiy Mishchenko
  """ Quasiclass for tagging resources
586 441e7cfd Oleksiy Mishchenko

587 c8e0a534 Iustin Pop
  Manages tags. When inheriting this class you must define the
588 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL for it.
589 441e7cfd Oleksiy Mishchenko

590 441e7cfd Oleksiy Mishchenko
  """
591 7a6b9510 Iustin Pop
  TAG_LEVEL = None
592 18cb43a2 Oleksiy Mishchenko
593 18cb43a2 Oleksiy Mishchenko
  def __init__(self, items, queryargs, req):
594 18cb43a2 Oleksiy Mishchenko
    """A tag resource constructor.
595 18cb43a2 Oleksiy Mishchenko

596 18cb43a2 Oleksiy Mishchenko
    We have to override the default to sort out cluster naming case.
597 18cb43a2 Oleksiy Mishchenko

598 18cb43a2 Oleksiy Mishchenko
    """
599 18cb43a2 Oleksiy Mishchenko
    baserlib.R_Generic.__init__(self, items, queryargs, req)
600 18cb43a2 Oleksiy Mishchenko
601 18cb43a2 Oleksiy Mishchenko
    if self.TAG_LEVEL != constants.TAG_CLUSTER:
602 18cb43a2 Oleksiy Mishchenko
      self.name = items[0]
603 18cb43a2 Oleksiy Mishchenko
    else:
604 18cb43a2 Oleksiy Mishchenko
      self.name = ""
605 441e7cfd Oleksiy Mishchenko
606 441e7cfd Oleksiy Mishchenko
  def GET(self):
607 18cb43a2 Oleksiy Mishchenko
    """Returns a list of tags.
608 441e7cfd Oleksiy Mishchenko

609 441e7cfd Oleksiy Mishchenko
    Example: ["tag1", "tag2", "tag3"]
610 441e7cfd Oleksiy Mishchenko

611 441e7cfd Oleksiy Mishchenko
    """
612 18cb43a2 Oleksiy Mishchenko
    return baserlib._Tags_GET(self.TAG_LEVEL, name=self.name)
613 441e7cfd Oleksiy Mishchenko
614 21f04e5e Oleksiy Mishchenko
  def PUT(self):
615 18cb43a2 Oleksiy Mishchenko
    """Add a set of tags.
616 441e7cfd Oleksiy Mishchenko

617 c41eea6e Iustin Pop
    The request as a list of strings should be PUT to this URI. And
618 c41eea6e Iustin Pop
    you'll have back a job id.
619 441e7cfd Oleksiy Mishchenko

620 441e7cfd Oleksiy Mishchenko
    """
621 6e99c5a0 Iustin Pop
    if 'tag' not in self.queryargs:
622 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Please specify tag(s) to add using the"
623 6e99c5a0 Iustin Pop
                                " the 'tag' parameter")
624 18cb43a2 Oleksiy Mishchenko
    return baserlib._Tags_PUT(self.TAG_LEVEL,
625 6f59b964 Iustin Pop
                              self.queryargs['tag'], name=self.name,
626 6f59b964 Iustin Pop
                              dry_run=bool(self.dryRun()))
627 15fd9fd5 Oleksiy Mishchenko
628 15fd9fd5 Oleksiy Mishchenko
  def DELETE(self):
629 15fd9fd5 Oleksiy Mishchenko
    """Delete a tag.
630 15fd9fd5 Oleksiy Mishchenko

631 18cb43a2 Oleksiy Mishchenko
    In order to delete a set of tags, the DELETE
632 c41eea6e Iustin Pop
    request should be addressed to URI like:
633 18cb43a2 Oleksiy Mishchenko
    /tags?tag=[tag]&tag=[tag]
634 15fd9fd5 Oleksiy Mishchenko

635 15fd9fd5 Oleksiy Mishchenko
    """
636 15fd9fd5 Oleksiy Mishchenko
    if 'tag' not in self.queryargs:
637 18cb43a2 Oleksiy Mishchenko
      # no we not gonna delete all tags
638 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Cannot delete all tags - please specify"
639 6e99c5a0 Iustin Pop
                                " tag(s) using the 'tag' parameter")
640 18cb43a2 Oleksiy Mishchenko
    return baserlib._Tags_DELETE(self.TAG_LEVEL,
641 15fd9fd5 Oleksiy Mishchenko
                                 self.queryargs['tag'],
642 6f59b964 Iustin Pop
                                 name=self.name,
643 6f59b964 Iustin Pop
                                 dry_run=bool(self.dryRun()))
644 18cb43a2 Oleksiy Mishchenko
645 18cb43a2 Oleksiy Mishchenko
646 18cb43a2 Oleksiy Mishchenko
class R_2_instances_name_tags(_R_Tags):
647 18cb43a2 Oleksiy Mishchenko
  """ /2/instances/[instance_name]/tags resource.
648 18cb43a2 Oleksiy Mishchenko

649 18cb43a2 Oleksiy Mishchenko
  Manages per-instance tags.
650 18cb43a2 Oleksiy Mishchenko

651 18cb43a2 Oleksiy Mishchenko
  """
652 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_INSTANCE
653 18cb43a2 Oleksiy Mishchenko
654 18cb43a2 Oleksiy Mishchenko
655 18cb43a2 Oleksiy Mishchenko
class R_2_nodes_name_tags(_R_Tags):
656 18cb43a2 Oleksiy Mishchenko
  """ /2/nodes/[node_name]/tags resource.
657 18cb43a2 Oleksiy Mishchenko

658 18cb43a2 Oleksiy Mishchenko
  Manages per-node tags.
659 18cb43a2 Oleksiy Mishchenko

660 18cb43a2 Oleksiy Mishchenko
  """
661 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_NODE
662 18cb43a2 Oleksiy Mishchenko
663 18cb43a2 Oleksiy Mishchenko
664 18cb43a2 Oleksiy Mishchenko
class R_2_tags(_R_Tags):
665 18cb43a2 Oleksiy Mishchenko
  """ /2/instances/tags resource.
666 18cb43a2 Oleksiy Mishchenko

667 18cb43a2 Oleksiy Mishchenko
  Manages cluster tags.
668 18cb43a2 Oleksiy Mishchenko

669 18cb43a2 Oleksiy Mishchenko
  """
670 18cb43a2 Oleksiy Mishchenko
  TAG_LEVEL = constants.TAG_CLUSTER