Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / rlib2.py @ 495cfdf0

History | View | Annotate | Download (11.8 kB)

1
#
2
#
3

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

    
21

    
22
"""Remote API version 2 baserlib.library.
23

24
"""
25

    
26
from ganeti import opcodes
27
from ganeti import http
28
from ganeti import constants
29
from ganeti import cli
30
from ganeti.rapi import baserlib
31

    
32

    
33

    
34
I_FIELDS = ["name", "admin_state", "os",
35
            "pnode", "snodes",
36
            "disk_template",
37
            "nic.ips", "nic.macs", "nic.modes", "nic.links",
38
            "network_port",
39
            "disk.sizes", "disk_usage",
40
            "beparams", "hvparams",
41
            "oper_state", "oper_ram", "status",
42
            "tags"]
43

    
44
N_FIELDS = ["name", "offline", "master_candidate", "drained",
45
            "dtotal", "dfree",
46
            "mtotal", "mnode", "mfree",
47
            "pinst_cnt", "sinst_cnt", "tags",
48
            "ctotal", "cnodes", "csockets",
49
            ]
50

    
51

    
52
class R_version(baserlib.R_Generic):
53
  """/version resource.
54

55
  This resource should be used to determine the remote API version and
56
  to adapt clients accordingly.
57

58
  """
59
  def GET(self):
60
    """Returns the remote API version.
61

62
    """
63
    return constants.RAPI_VERSION
64

    
65

    
66
class R_2_info(baserlib.R_Generic):
67
  """Cluster info.
68

69
  """
70
  def GET(self):
71
    """Returns cluster information.
72

73
    """
74
    client = baserlib.GetClient()
75
    return client.QueryClusterInfo()
76

    
77

    
78
class R_2_os(baserlib.R_Generic):
79
  """/2/os resource.
80

81
  """
82
  def GET(self):
83
    """Return a list of all OSes.
84

85
    Can return error 500 in case of a problem.
86

87
    Example: ["debian-etch"]
88

89
    """
90
    cl = baserlib.GetClient()
91
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
92
    job_id = baserlib.SubmitJob([op], cl)
93
    # we use custom feedback function, instead of print we log the status
94
    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
95
    diagnose_data = result[0]
96

    
97
    if not isinstance(diagnose_data, list):
98
      raise http.HttpBadGateway(message="Can't get OS list")
99

    
100
    return [row[0] for row in diagnose_data if row[1]]
101

    
102

    
103
class R_2_jobs(baserlib.R_Generic):
104
  """/2/jobs resource.
105

106
  """
107
  def GET(self):
108
    """Returns a dictionary of jobs.
109

110
    @return: a dictionary with jobs id and uri.
111

112
    """
113
    fields = ["id"]
114
    cl = baserlib.GetClient()
115
    # Convert the list of lists to the list of ids
116
    result = [job_id for [job_id] in cl.QueryJobs(None, fields)]
117
    return baserlib.BuildUriList(result, "/2/jobs/%s",
118
                                 uri_fields=("id", "uri"))
119

    
120

    
121
class R_2_jobs_id(baserlib.R_Generic):
122
  """/2/jobs/[job_id] resource.
123

124
  """
125
  def GET(self):
126
    """Returns a job status.
127

128
    @return: a dictionary with job parameters.
129
        The result includes:
130
            - id: job ID as a number
131
            - status: current job status as a string
132
            - ops: involved OpCodes as a list of dictionaries for each
133
              opcodes in the job
134
            - opstatus: OpCodes status as a list
135
            - opresult: OpCodes results as a list of lists
136

137
    """
138
    fields = ["id", "ops", "status", "summary",
139
              "opstatus", "opresult", "oplog",
140
              "received_ts", "start_ts", "end_ts",
141
              ]
142
    job_id = self.items[0]
143
    result = baserlib.GetClient().QueryJobs([job_id, ], fields)[0]
144
    if result is None:
145
      raise http.HttpNotFound()
146
    return baserlib.MapFields(fields, result)
147

    
148
  def DELETE(self):
149
    """Cancel not-yet-started job.
150

151
    """
152
    job_id = self.items[0]
153
    result = baserlib.GetClient().CancelJob(job_id)
154
    return result
155

    
156

    
157
class R_2_nodes(baserlib.R_Generic):
158
  """/2/nodes resource.
159

160
  """
161
  def GET(self):
162
    """Returns a list of all nodes.
163

164
    """
165
    client = baserlib.GetClient()
166

    
167
    if self.useBulk():
168
      bulkdata = client.QueryNodes([], N_FIELDS, False)
169
      return baserlib.MapBulkFields(bulkdata, N_FIELDS)
170
    else:
171
      nodesdata = client.QueryNodes([], ["name"], False)
172
      nodeslist = [row[0] for row in nodesdata]
173
      return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
174
                                   uri_fields=("id", "uri"))
175

    
176

    
177
class R_2_nodes_name(baserlib.R_Generic):
178
  """/2/nodes/[node_name] resources.
179

180
  """
181
  def GET(self):
182
    """Send information about a node.
183

184
    """
185
    node_name = self.items[0]
186
    client = baserlib.GetClient()
187
    result = client.QueryNodes(names=[node_name], fields=N_FIELDS,
188
                               use_locking=self.useLocking())
189

    
190
    return baserlib.MapFields(N_FIELDS, result[0])
191

    
192

    
193
class R_2_instances(baserlib.R_Generic):
194
  """/2/instances resource.
195

196
  """
197
  def GET(self):
198
    """Returns a list of all available instances.
199

200
    """
201
    client = baserlib.GetClient()
202

    
203
    use_locking = self.useLocking()
204
    if self.useBulk():
205
      bulkdata = client.QueryInstances([], I_FIELDS, use_locking)
206
      return baserlib.MapBulkFields(bulkdata, I_FIELDS)
207
    else:
208
      instancesdata = client.QueryInstances([], ["name"], use_locking)
209
      instanceslist = [row[0] for row in instancesdata]
210
      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
211
                                   uri_fields=("id", "uri"))
212

    
213
  def POST(self):
214
    """Create an instance.
215

216
    @return: a job id
217

218
    """
219
    if not isinstance(self.req.request_body, dict):
220
      raise http.HttpBadRequest("Invalid body contents, not a dictionary")
221

    
222
    beparams = baserlib.MakeParamsDict(self.req.request_body,
223
                                       constants.BES_PARAMETERS)
224
    hvparams = baserlib.MakeParamsDict(self.req.request_body,
225
                                       constants.HVS_PARAMETERS)
226
    fn = self.getBodyParameter
227

    
228
    # disk processing
229
    disk_data = fn('disks')
230
    if not isinstance(disk_data, list):
231
      raise http.HttpBadRequest("The 'disks' parameter should be a list")
232
    disks = []
233
    for idx, d in enumerate(disk_data):
234
      if not isinstance(d, int):
235
        raise http.HttpBadRequest("Disk %d specification wrong: should"
236
                                  " be an integer")
237
      disks.append({"size": d})
238
    # nic processing (one nic only)
239
    nics = [{"mac": fn("mac", constants.VALUE_AUTO)}]
240
    if fn("ip", None) is not None:
241
      nics[0]["ip"] = fn("ip")
242
    if fn("mode", None) is not None:
243
      nics[0]["mode"] = fn("mode")
244
    if fn("link", None) is not None:
245
      nics[0]["link"] = fn("link")
246
    if fn("bridge", None) is not None:
247
       nics[0]["bridge"] = fn("bridge")
248

    
249
    op = opcodes.OpCreateInstance(
250
      mode=constants.INSTANCE_CREATE,
251
      instance_name=fn('name'),
252
      disks=disks,
253
      disk_template=fn('disk_template'),
254
      os_type=fn('os'),
255
      pnode=fn('pnode', None),
256
      snode=fn('snode', None),
257
      iallocator=fn('iallocator', None),
258
      nics=nics,
259
      start=fn('start', True),
260
      ip_check=fn('ip_check', True),
261
      wait_for_sync=True,
262
      hypervisor=fn('hypervisor', None),
263
      hvparams=hvparams,
264
      beparams=beparams,
265
      file_storage_dir=fn('file_storage_dir', None),
266
      file_driver=fn('file_driver', 'loop'),
267
      )
268

    
269
    return baserlib.SubmitJob([op])
270

    
271

    
272
class R_2_instances_name(baserlib.R_Generic):
273
  """/2/instances/[instance_name] resources.
274

275
  """
276
  def GET(self):
277
    """Send information about an instance.
278

279
    """
280
    client = baserlib.GetClient()
281
    instance_name = self.items[0]
282
    result = client.QueryInstances(names=[instance_name], fields=I_FIELDS,
283
                                   use_locking=self.useLocking())
284

    
285
    return baserlib.MapFields(I_FIELDS, result[0])
286

    
287
  def DELETE(self):
288
    """Delete an instance.
289

290
    """
291
    op = opcodes.OpRemoveInstance(instance_name=self.items[0],
292
                                  ignore_failures=False)
293
    return baserlib.SubmitJob([op])
294

    
295

    
296
class R_2_instances_name_reboot(baserlib.R_Generic):
297
  """/2/instances/[instance_name]/reboot resource.
298

299
  Implements an instance reboot.
300

301
  """
302
  def POST(self):
303
    """Reboot an instance.
304

305
    The URI takes type=[hard|soft|full] and
306
    ignore_secondaries=[False|True] parameters.
307

308
    """
309
    instance_name = self.items[0]
310
    reboot_type = self.queryargs.get('type',
311
                                     [constants.INSTANCE_REBOOT_HARD])[0]
312
    ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
313
                                                 [False])[0])
314
    op = opcodes.OpRebootInstance(instance_name=instance_name,
315
                                  reboot_type=reboot_type,
316
                                  ignore_secondaries=ignore_secondaries)
317

    
318
    return baserlib.SubmitJob([op])
319

    
320

    
321
class R_2_instances_name_startup(baserlib.R_Generic):
322
  """/2/instances/[instance_name]/startup resource.
323

324
  Implements an instance startup.
325

326
  """
327
  def PUT(self):
328
    """Startup an instance.
329

330
    The URI takes force=[False|True] parameter to start the instance
331
    if even if secondary disks are failing.
332

333
    """
334
    instance_name = self.items[0]
335
    force_startup = bool(self.queryargs.get('force', [False])[0])
336
    op = opcodes.OpStartupInstance(instance_name=instance_name,
337
                                   force=force_startup)
338

    
339
    return baserlib.SubmitJob([op])
340

    
341

    
342
class R_2_instances_name_shutdown(baserlib.R_Generic):
343
  """/2/instances/[instance_name]/shutdown resource.
344

345
  Implements an instance shutdown.
346

347
  """
348
  def PUT(self):
349
    """Shutdown an instance.
350

351
    """
352
    instance_name = self.items[0]
353
    op = opcodes.OpShutdownInstance(instance_name=instance_name)
354

    
355
    return baserlib.SubmitJob([op])
356

    
357

    
358
class _R_Tags(baserlib.R_Generic):
359
  """ Quasiclass for tagging resources
360

361
  Manages tags. When inheriting this class you must define the
362
  TAG_LEVEL for it.
363

364
  """
365
  TAG_LEVEL = None
366

    
367
  def __init__(self, items, queryargs, req):
368
    """A tag resource constructor.
369

370
    We have to override the default to sort out cluster naming case.
371

372
    """
373
    baserlib.R_Generic.__init__(self, items, queryargs, req)
374

    
375
    if self.TAG_LEVEL != constants.TAG_CLUSTER:
376
      self.name = items[0]
377
    else:
378
      self.name = ""
379

    
380
  def GET(self):
381
    """Returns a list of tags.
382

383
    Example: ["tag1", "tag2", "tag3"]
384

385
    """
386
    return baserlib._Tags_GET(self.TAG_LEVEL, name=self.name)
387

    
388
  def PUT(self):
389
    """Add a set of tags.
390

391
    The request as a list of strings should be PUT to this URI. And
392
    you'll have back a job id.
393

394
    """
395
    if 'tag' not in self.queryargs:
396
      raise http.HttpBadRequest("Please specify tag(s) to add using the"
397
                                " the 'tag' parameter")
398
    return baserlib._Tags_PUT(self.TAG_LEVEL,
399
                              self.queryargs['tag'], name=self.name)
400

    
401
  def DELETE(self):
402
    """Delete a tag.
403

404
    In order to delete a set of tags, the DELETE
405
    request should be addressed to URI like:
406
    /tags?tag=[tag]&tag=[tag]
407

408
    """
409
    if 'tag' not in self.queryargs:
410
      # no we not gonna delete all tags
411
      raise http.HttpBadRequest("Cannot delete all tags - please specify"
412
                                " tag(s) using the 'tag' parameter")
413
    return baserlib._Tags_DELETE(self.TAG_LEVEL,
414
                                 self.queryargs['tag'],
415
                                 name=self.name)
416

    
417

    
418
class R_2_instances_name_tags(_R_Tags):
419
  """ /2/instances/[instance_name]/tags resource.
420

421
  Manages per-instance tags.
422

423
  """
424
  TAG_LEVEL = constants.TAG_INSTANCE
425

    
426

    
427
class R_2_nodes_name_tags(_R_Tags):
428
  """ /2/nodes/[node_name]/tags resource.
429

430
  Manages per-node tags.
431

432
  """
433
  TAG_LEVEL = constants.TAG_NODE
434

    
435

    
436
class R_2_tags(_R_Tags):
437
  """ /2/instances/tags resource.
438

439
  Manages cluster tags.
440

441
  """
442
  TAG_LEVEL = constants.TAG_CLUSTER