Revision c909cbbd

b/README.upgrade
5 5
For more information, please see README.deploy.
6 6

  
7 7

  
8
v0.7 -> v0.8
9
API
10
    * A new 'disk_template' attribute has been added to Flavors.
11
      GANETI_DISK_TEMPLATES and DEFAULT_GANETI_DISK_TEMPLATE have been added
12
      in 20-api.conf to control its value. A database migration is needed.
13

  
14

  
8 15
v0.6.2 -> v0.7
9 16
HTML TEMPLATES
10 17
    * Included a generic service unavailable template based on
b/admin/templates/flavors_info.html
45 45
  </div>
46 46

  
47 47
  <div class="clearfix">
48
    <label for="flavor-disk-template">State</label>
49
    <div class="input">
50
      <select class="medium" id="flavor-disk-template" name="disk_template">
51
        {% for disk_template in disk_templates %}
52
        <option{% ifequal disk_template flavor.disk_template %} selected{% endifequal %}>{{ disk_template }}</option>
53
        {% endfor %}
54
      </select>
55
    </div>
56
  </div>
57

  
58
  <div class="clearfix">
48 59
    <label for="flavor-deleted">Deleted</label>
49 60
    <div class="input">
50 61
      <ul class="inputs-list">
b/admin/views.py
33 33

  
34 34
from functools import wraps
35 35

  
36
from django.conf import settings
36 37
from django.http import HttpResponse
37 38
from django.shortcuts import redirect
38 39
from django.template.loader import render_to_string
......
134 135
@requires_admin
135 136
def flavors_info(request, flavor_id):
136 137
    flavor = models.Flavor.objects.get(id=flavor_id)
137
    html = render('flavors_info.html', 'flavors', flavor=flavor)
138
    html = render('flavors_info.html', 'flavors',
139
                    flavor=flavor,
140
                    disk_templates=settings.GANETI_DISK_TEMPLATES)
138 141
    return HttpResponse(html)
139 142

  
140 143

  
......
144 147
    flavor.cpu = int(request.POST.get('cpu'))
145 148
    flavor.ram = int(request.POST.get('ram'))
146 149
    flavor.disk = int(request.POST.get('disk'))
150
    flavor.disk_template = request.POST.get('disk_template')
147 151
    flavor.deleted = True if request.POST.get('deleted') else False
148 152
    flavor.save()
149 153
    log.info('User %s modified Flavor %s', request.user.name, flavor.name)
b/api/flavors.py
57 57
        d['ram'] = flavor.ram
58 58
        d['disk'] = flavor.disk
59 59
        d['cpu'] = flavor.cpu
60
        d['SNF:disk_template'] = flavor.disk_template
60 61
    return d
61 62

  
62 63

  
b/db/migrations/0022_auto__add_field_flavor_disk_template.py
1
# encoding: utf-8
2
import datetime
3
from south.db import db
4
from south.v2 import SchemaMigration
5
from django.db import models
6

  
7
class Migration(SchemaMigration):
8

  
9
    def forwards(self, orm):
10
        
11
        # Adding field 'Flavor.disk_template'
12
        db.add_column('db_flavor', 'disk_template', self.gf('django.db.models.fields.CharField')(default='drbd', max_length=32), keep_default=False)
13

  
14

  
15
    def backwards(self, orm):
16
        
17
        # Deleting field 'Flavor.disk_template'
18
        db.delete_column('db_flavor', 'disk_template')
19

  
20

  
21
    models = {
22
        'db.debit': {
23
            'Meta': {'object_name': 'Debit'},
24
            'description': ('django.db.models.fields.TextField', [], {}),
25
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
27
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"}),
28
            'when': ('django.db.models.fields.DateTimeField', [], {})
29
        },
30
        'db.disk': {
31
            'Meta': {'object_name': 'Disk'},
32
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
33
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
35
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
36
            'size': ('django.db.models.fields.PositiveIntegerField', [], {}),
37
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
38
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True', 'blank': 'True'})
39
        },
40
        'db.flavor': {
41
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk'),)", 'object_name': 'Flavor'},
42
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
43
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
44
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
45
            'disk_template': ('django.db.models.fields.CharField', [], {'default': "'drbd'", 'max_length': '32'}),
46
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
47
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
48
        },
49
        'db.flavorcost': {
50
            'Meta': {'object_name': 'FlavorCost'},
51
            'cost_active': ('django.db.models.fields.PositiveIntegerField', [], {}),
52
            'cost_inactive': ('django.db.models.fields.PositiveIntegerField', [], {}),
53
            'effective_from': ('django.db.models.fields.DateTimeField', [], {}),
54
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
55
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
56
        },
57
        'db.image': {
58
            'Meta': {'object_name': 'Image'},
59
            'backend_id': ('django.db.models.fields.CharField', [], {'default': "'debian_base'", 'max_length': '50'}),
60
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
61
            'format': ('django.db.models.fields.CharField', [], {'default': "'dump'", 'max_length': '30'}),
62
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
64
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
65
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
66
            'sourcevm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True'}),
67
            'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
68
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
69
        },
70
        'db.imagemetadata': {
71
            'Meta': {'unique_together': "(('meta_key', 'image'),)", 'object_name': 'ImageMetadata'},
72
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
73
            'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.Image']"}),
74
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
75
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'})
76
        },
77
        'db.invitations': {
78
            'Meta': {'object_name': 'Invitations'},
79
            'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
80
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
81
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82
            'level': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
83
            'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source'", 'to': "orm['db.SynnefoUser']"}),
84
            'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'target'", 'to': "orm['db.SynnefoUser']"}),
85
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
86
        },
87
        'db.limit': {
88
            'Meta': {'object_name': 'Limit'},
89
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
90
            'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
91
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
92
            'value': ('django.db.models.fields.IntegerField', [], {})
93
        },
94
        'db.network': {
95
            'Meta': {'object_name': 'Network'},
96
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
97
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
98
            'link': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['db.NetworkLink']"}),
99
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
100
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
101
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True'}),
102
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
103
            'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
104
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
105
        },
106
        'db.networkinterface': {
107
            'Meta': {'object_name': 'NetworkInterface'},
108
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
109
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
110
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
111
            'index': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
112
            'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
113
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
114
            'mac': ('django.db.models.fields.CharField', [], {'max_length': '17', 'null': 'True'}),
115
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
116
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
117
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
118
        },
119
        'db.networklink': {
120
            'Meta': {'object_name': 'NetworkLink'},
121
            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
122
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123
            'index': ('django.db.models.fields.IntegerField', [], {}),
124
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
125
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['db.Network']"})
126
        },
127
        'db.synnefouser': {
128
            'Meta': {'object_name': 'SynnefoUser'},
129
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
130
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
131
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
132
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
133
            'credit': ('django.db.models.fields.IntegerField', [], {}),
134
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
135
            'max_invitations': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
136
            'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
137
            'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
138
            'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '30'}),
139
            'tmp_auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
140
            'tmp_auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
141
            'type': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
142
            'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
143
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
144
        },
145
        'db.virtualmachine': {
146
            'Meta': {'object_name': 'VirtualMachine'},
147
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
148
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
149
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
150
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
151
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
152
            'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
153
            'charged': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 11, 10, 15, 26, 46, 658284)'}),
154
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
155
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
156
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
157
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
158
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
160
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
161
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
162
            'sourceimage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
163
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
164
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
165
        },
166
        'db.virtualmachinegroup': {
167
            'Meta': {'ordering': "['name']", 'object_name': 'VirtualMachineGroup'},
168
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
169
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
170
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'symmetrical': 'False'}),
171
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
172
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
173
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
174
        },
175
        'db.virtualmachinemetadata': {
176
            'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'},
177
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
178
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
179
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
180
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"})
181
        }
182
    }
183

  
184
    complete_apps = ['db']
b/db/models.py
169 169

  
170 170
class Flavor(models.Model):
171 171
    cpu = models.IntegerField('Number of CPUs', default=0)
172
    ram = models.IntegerField('Size of RAM', default=0)             # Size in MiB
173
    disk = models.IntegerField('Size of Disk space', default=0)     # Size in GiB
172
    ram = models.IntegerField('RAM size in MiB', default=0)
173
    disk = models.IntegerField('Disk size in GiB', default=0)
174
    disk_template = models.CharField('Disk template', max_length=32,
175
                                default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
174 176
    deleted = models.BooleanField('Deleted', default=False)
175 177
    
176 178
    class Meta:
b/settings.d/20-api.conf
69 69

  
70 70
# The maximum size, in bytes, for each personality file
71 71
MAX_PERSONALITY_SIZE = 10240
72

  
73
# Available storage types to be used as disk templates
74
GANETI_DISK_TEMPLATES = ('blockdev', 'diskless', 'drbd', 'file', 'plain',
75
                         'rbd',  'sharedfile')
76
DEFAULT_GANETI_DISK_TEMPLATE = 'drbd'
b/snf-tools/snf-admin
465 465
    syntax = '<cpu>[,<cpu>,...] <ram>[,<ram>,...] <disk>[,<disk>,...]'
466 466
    description = 'create one or more flavors'
467 467
    
468
    def add_options(self, parser):
469
        disk_templates = ', '.join(t for t in settings.GANETI_DISK_TEMPLATES)
470
        parser.add_option('--disk-template',
471
            dest='disk_template',
472
            metavar='TEMPLATE',
473
            default=settings.DEFAULT_GANETI_DISK_TEMPLATE,
474
            help='available disk templates: %s' % disk_templates)
475
    
468 476
    def main(self, cpu, ram, disk):
469 477
        cpus = cpu.split(',')
470 478
        rams = ram.split(',')
......
479 487
                return
480 488
        
481 489
        created = []
490
        
482 491
        for cpu, ram, disk in flavors:
483
            flavor = models.Flavor.objects.create(cpu=cpu, ram=ram, disk=disk)
492
            flavor = models.Flavor.objects.create(
493
                cpu=cpu,
494
                ram=ram,
495
                disk=disk,
496
                disk_template=self.disk_template)
484 497
            created.append(flavor)
485 498
        
486 499
        print_items(created, detail=True)

Also available in: Unified diff