Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (12.3 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
import ganeti.opcodes
27
from ganeti import http
28
from ganeti import luxi
29
from ganeti import constants
30
from ganeti.rapi import baserlib
31

    
32

    
33
I_FIELDS = ["name", "os", "pnode", "snodes", "admin_state", "disk_template",
34
            "ip", "mac", "bridge", "sda_size", "sdb_size", "beparams",
35
            "oper_state", "status", "tags"]
36

    
37
N_FIELDS = ["name", "dtotal", "dfree",
38
            "mtotal", "mnode", "mfree",
39
            "pinst_cnt", "sinst_cnt", "tags"]
40

    
41

    
42
class R_version(baserlib.R_Generic):
43
  """/version resource.
44

45
  This resource should be used to determine the remote API version and
46
  to adapt clients accordingly.
47

48
  """
49
  DOC_URI = "/version"
50

    
51
  def GET(self):
52
    """Returns the remote API version.
53

54
    """
55
    return constants.RAPI_VERSION
56

    
57

    
58
class R_2_info(baserlib.R_Generic):
59
  """Cluster info.
60

61
  """
62
  DOC_URI = "/2/info"
63

    
64
  def GET(self):
65
    """Returns cluster information.
66

67
    Example::
68

69
      {
70
        "config_version": 3,
71
        "name": "cluster1.example.com",
72
        "software_version": "1.2.4",
73
        "os_api_version": 5,
74
        "export_version": 0,
75
        "master": "node1.example.com",
76
        "architecture": [
77
          "64bit",
78
          "x86_64"
79
        ],
80
        "hypervisor_type": "xen-pvm",
81
        "protocol_version": 12
82
      }
83

84
    """
85
    op = ganeti.opcodes.OpQueryClusterInfo()
86
    return ganeti.cli.SubmitOpCode(op)
87

    
88

    
89
class R_2_tags(baserlib.R_Generic):
90
  """/2/tags resource.
91

92
  Manages cluster tags.
93

94
  """
95
  DOC_URI = "/2/tags"
96

    
97
  def GET(self):
98
    """Returns a list of all cluster tags.
99

100
    Example: ["tag1", "tag2", "tag3"]
101

102
    """
103
    return baserlib._Tags_GET(constants.TAG_CLUSTER)
104

    
105

    
106
class R_2_os(baserlib.R_Generic):
107
  """/2/os resource.
108

109
  """
110
  DOC_URI = "/2/os"
111

    
112
  def GET(self):
113
    """Return a list of all OSes.
114

115
    Can return error 500 in case of a problem.
116

117
    Example: ["debian-etch"]
118

119
    """
120
    op = ganeti.opcodes.OpDiagnoseOS(output_fields=["name", "valid"],
121
                                     names=[])
122
    diagnose_data = ganeti.cli.SubmitOpCode(op)
123

    
124
    if not isinstance(diagnose_data, list):
125
      raise http.HttpInternalServerError(message="Can't get OS list")
126

    
127
    return [row[0] for row in diagnose_data if row[1]]
128

    
129

    
130
class R_2_jobs(baserlib.R_Generic):
131
  """/2/jobs resource.
132

133
  """
134
  DOC_URI = "/2/jobs"
135

    
136
  def GET(self):
137
    """Returns a dictionary of jobs.
138

139
    @return: a dictionary with jobs id and uri.
140

141
    """
142
    fields = ["id"]
143
    # Convert the list of lists to the list of ids
144
    result = [job_id for [job_id] in luxi.Client().QueryJobs(None, fields)]
145
    return baserlib.BuildUriList(result, "/2/jobs/%s", uri_fields=("id", "uri"))
146

    
147

    
148
class R_2_jobs_id(baserlib.R_Generic):
149
  """/2/jobs/[job_id] resource.
150

151
  """
152
  DOC_URI = "/2/jobs/[job_id]"
153

    
154
  def GET(self):
155
    """Returns a job status.
156

157
    @return: a dictionary with job parameters.
158
        The result includes:
159
            - id: job ID as a number
160
            - status: current job status as a string
161
            - ops: involved OpCodes as a list of dictionaries for each
162
              opcodes in the job
163
            - opstatus: OpCodes status as a list
164
            - opresult: OpCodes results as a list of lists
165

166
    """
167
    fields = ["id", "ops", "status", "opstatus", "opresult"]
168
    job_id = self.items[0]
169
    result = luxi.Client().QueryJobs([job_id, ], fields)[0]
170
    return baserlib.MapFields(fields, result)
171

    
172
  def DELETE(self):
173
    """Cancel not-yet-started job.
174

175
    """
176
    job_id = self.items[0]
177
    result = luxi.Client().CancelJob(job_id)
178
    return result
179

    
180

    
181
class R_2_nodes(baserlib.R_Generic):
182
  """/2/nodes resource.
183

184
  """
185
  DOC_URI = "/2/nodes"
186

    
187
  def GET(self):
188
    """Returns a list of all nodes.
189

190
    Example::
191

192
      [
193
        {
194
          "id": "node1.example.com",
195
          "uri": "\/instances\/node1.example.com"
196
        },
197
        {
198
          "id": "node2.example.com",
199
          "uri": "\/instances\/node2.example.com"
200
        }
201
      ]
202

203
    If the optional 'bulk' argument is provided and set to 'true'
204
    value (i.e '?bulk=1'), the output contains detailed
205
    information about nodes as a list.
206

207
    Example::
208

209
      [
210
        {
211
          "pinst_cnt": 1,
212
          "mfree": 31280,
213
          "mtotal": 32763,
214
          "name": "www.example.com",
215
          "tags": [],
216
          "mnode": 512,
217
          "dtotal": 5246208,
218
          "sinst_cnt": 2,
219
          "dfree": 5171712
220
        },
221
        ...
222
      ]
223

224
    @return: a dictionary with 'name' and 'uri' keys for each of them
225

226
    """
227
    client = luxi.Client()
228
    nodesdata = client.QueryNodes([], ["name"])
229
    nodeslist = [row[0] for row in nodesdata]
230

    
231
    if 'bulk' in self.queryargs:
232
      bulkdata = client.QueryNodes(nodeslist, N_FIELDS)
233
      return baserlib.MapBulkFields(bulkdata, N_FIELDS)
234

    
235
    return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
236
                                 uri_fields=("id", "uri"))
237

    
238

    
239
class R_2_nodes_name(baserlib.R_Generic):
240
  """/2/nodes/[node_name] resources.
241

242
  """
243
  DOC_URI = "/nodes/[node_name]"
244

    
245
  def GET(self):
246
    """Send information about a node.
247

248
    """
249
    node_name = self.items[0]
250
    op = ganeti.opcodes.OpQueryNodes(output_fields=N_FIELDS,
251
                                     names=[node_name])
252
    result = ganeti.cli.SubmitOpCode(op)
253

    
254
    return baserlib.MapFields(N_FIELDS, result[0])
255

    
256

    
257
class R_2_instances(baserlib.R_Generic):
258
  """/2/instances resource.
259

260
  """
261
  DOC_URI = "/2/instances"
262

    
263
  def GET(self):
264
    """Returns a list of all available instances.
265

266

267
    Example::
268

269
      [
270
        {
271
          "name": "web.example.com",
272
          "uri": "\/instances\/web.example.com"
273
        },
274
        {
275
          "name": "mail.example.com",
276
          "uri": "\/instances\/mail.example.com"
277
        }
278
      ]
279

280
    If the optional 'bulk' argument is provided and set to 'true'
281
    value (i.e '?bulk=1'), the output contains detailed
282
    information about instances as a list.
283

284
    Example::
285

286
      [
287
        {
288
           "status": "running",
289
           "bridge": "xen-br0",
290
           "name": "web.example.com",
291
           "tags": ["tag1", "tag2"],
292
           "admin_ram": 512,
293
           "sda_size": 20480,
294
           "pnode": "node1.example.com",
295
           "mac": "01:23:45:67:89:01",
296
           "sdb_size": 4096,
297
           "snodes": ["node2.example.com"],
298
           "disk_template": "drbd",
299
           "ip": null,
300
           "admin_state": true,
301
           "os": "debian-etch",
302
           "vcpus": 2,
303
           "oper_state": true
304
        },
305
        ...
306
      ]
307

308
    @returns: a dictionary with 'name' and 'uri' keys for each of them.
309

310
    """
311
    client = luxi.Client()
312
    instancesdata = client.QueryInstances([], ["name"])
313
    instanceslist = [row[0] for row in instancesdata]
314

    
315

    
316
    if 'bulk' in self.queryargs:
317
      bulkdata = client.QueryInstances(instanceslist, I_FIELDS)
318
      return baserlib.MapBulkFields(bulkdata, I_FIELDS)
319

    
320
    else:
321
      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
322
                                   uri_fields=("id", "uri"))
323

    
324
  def POST(self):
325
    """Create an instance.
326

327
    @returns: a job id
328

329
    """
330
    opts = self.req.request_post_data
331

    
332
    beparams = baserlib.MakeParamsDict(opts, constants.BES_PARAMETERS)
333
    hvparams = baserlib.MakeParamsDict(opts, constants.HVS_PARAMETERS)
334

    
335
    op = ganeti.opcodes.OpCreateInstance(
336
        instance_name=opts.get('name'),
337
        disk_size=opts.get('size', 20 * 1024),
338
        swap_size=opts.get('swap', 4 * 1024),
339
        disk_template=opts.get('disk_template', None),
340
        mode=constants.INSTANCE_CREATE,
341
        os_type=opts.get('os'),
342
        pnode=opts.get('pnode'),
343
        snode=opts.get('snode'),
344
        ip=opts.get('ip', 'none'),
345
        bridge=opts.get('bridge', None),
346
        start=opts.get('start', True),
347
        ip_check=opts.get('ip_check', True),
348
        wait_for_sync=opts.get('wait_for_sync', True),
349
        mac=opts.get('mac', 'auto'),
350
        hypervisor=opts.get('hypervisor', None),
351
        hvparams=hvparams,
352
        beparams=beparams,
353
        iallocator=opts.get('iallocator', None),
354
        file_storage_dir=opts.get('file_storage_dir', None),
355
        file_driver=opts.get('file_driver', 'loop'),
356
        )
357

    
358
    job_id = ganeti.cli.SendJob([op])
359
    return job_id
360

    
361

    
362
class R_2_instances_name(baserlib.R_Generic):
363
  """/2/instances/[instance_name] resources.
364

365
  """
366
  DOC_URI = "/2/instances/[instance_name]"
367

    
368
  def GET(self):
369
    """Send information about an instance.
370

371
    """
372
    instance_name = self.items[0]
373
    op = ganeti.opcodes.OpQueryInstances(output_fields=I_FIELDS,
374
                                         names=[instance_name])
375
    result = ganeti.cli.SubmitOpCode(op)
376

    
377
    return baserlib.MapFields(I_FIELDS, result[0])
378

    
379

    
380
class R_2_instances_name_reboot(baserlib.R_Generic):
381
  """/2/instances/[instance_name]/reboot resource.
382

383
  Implements an instance reboot.
384

385
  """
386

    
387
  DOC_URI = "/2/instances/[instance_name]/reboot"
388

    
389
  def POST(self):
390
    """Reboot an instance.
391

392
    The URI takes type=[hard|soft|full] and
393
    ignore_secondaries=[False|True] parameters.
394

395
    """
396
    instance_name = self.items[0]
397
    reboot_type = self.queryargs.get('type',
398
                                     [constants.INSTANCE_REBOOT_HARD])[0]
399
    ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
400
                                                 [False])[0])
401
    op = ganeti.opcodes.OpRebootInstance(
402
        instance_name=instance_name,
403
        reboot_type=reboot_type,
404
        ignore_secondaries=ignore_secondaries)
405

    
406
    job_id = ganeti.cli.SendJob([op])
407

    
408
    return job_id
409

    
410

    
411
class R_2_instances_name_startup(baserlib.R_Generic):
412
  """/2/instances/[instance_name]/startup resource.
413

414
  Implements an instance startup.
415

416
  """
417

    
418
  DOC_URI = "/2/instances/[instance_name]/startup"
419

    
420
  def PUT(self):
421
    """Startup an instance.
422

423
    The URI takes force=[False|True] parameter to start the instance
424
    if even if secondary disks are failing.
425

426
    """
427
    instance_name = self.items[0]
428
    force_startup = bool(self.queryargs.get('force', [False])[0])
429
    op = ganeti.opcodes.OpStartupInstance(instance_name=instance_name,
430
                                          force=force_startup)
431

    
432
    job_id = ganeti.cli.SendJob([op])
433

    
434
    return job_id
435

    
436

    
437
class R_2_instances_name_shutdown(baserlib.R_Generic):
438
  """/2/instances/[instance_name]/shutdown resource.
439

440
  Implements an instance shutdown.
441

442
  """
443

    
444
  DOC_URI = "/2/instances/[instance_name]/shutdown"
445

    
446
  def PUT(self):
447
    """Shutdown an instance.
448

449
    """
450
    instance_name = self.items[0]
451
    op = ganeti.opcodes.OpShutdownInstance(instance_name=instance_name)
452

    
453
    job_id = ganeti.cli.SendJob([op])
454

    
455
    return job_id
456

    
457

    
458
class R_2_instances_name_tags(baserlib.R_Generic):
459
  """/2/instances/[instance_name]/tags resource.
460

461
  Manages per-instance tags.
462

463
  """
464
  DOC_URI = "/2/instances/[instance_name]/tags"
465

    
466
  def GET(self):
467
    """Returns a list of instance tags.
468

469
    Example: ["tag1", "tag2", "tag3"]
470

471
    """
472
    return baserlib._Tags_GET(constants.TAG_INSTANCE, name=self.items[0])
473

    
474
  def PUT(self):
475
    """Add a set of tags to the instance.
476

477
    The request as a list of strings should be PUT to this URI. And
478
    you'll have back a job id.
479

480
    """
481
    return baserlib._Tags_PUT(constants.TAG_INSTANCE,
482
                              self.req.request_post_data, name=self.items[0])
483

    
484
  def DELETE(self):
485
    """Delete a tag.
486

487
    In order to delete a set of tags from a instance, the DELETE
488
    request should be addressed to URI like:
489
    /2/instances/[instance_name]/tags?tag=[tag]&tag=[tag]
490

491
    """
492
    if 'tag' not in self.queryargs:
493
      # no we not gonna delete all tags from an instance
494
      raise http.HttpNotImplemented()
495
    return baserlib._Tags_DELETE(constants.TAG_INSTANCE,
496
                                 self.queryargs['tag'],
497
                                 name=self.items[0])