~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to nova/api/ec2/admin.py

  • Committer: NTT PF Lab.
  • Date: 2010-12-24 11:38:49 UTC
  • mto: This revision was merged to the branch mainline in revision 564.
  • Revision ID: openstack@lab.ntt.co.jp-20101224113849-z9nemzmki17bxnvw
SupportĀ IPv6

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
"""
22
22
 
23
23
import base64
24
 
import netaddr
25
 
import urllib
26
24
 
27
 
from nova import compute
28
25
from nova import db
29
26
from nova import exception
30
 
from nova import flags
31
 
from nova import log as logging
32
 
from nova import utils
33
 
from nova.api.ec2 import ec2utils
34
27
from nova.auth import manager
35
 
from nova.compute import vm_states
36
 
 
37
 
 
38
 
FLAGS = flags.FLAGS
39
 
LOG = logging.getLogger('nova.api.ec2.admin')
40
28
 
41
29
 
42
30
def user_dict(user, base64_file=None):
62
50
        return {}
63
51
 
64
52
 
65
 
def host_dict(host, compute_service, instances, volume_service, volumes, now):
 
53
def host_dict(host):
66
54
    """Convert a host model object to a result dict"""
67
 
    rv = {'hostname': host, 'instance_count': len(instances),
68
 
          'volume_count': len(volumes)}
69
 
    if compute_service:
70
 
        latest = compute_service['updated_at'] or compute_service['created_at']
71
 
        delta = now - latest
72
 
        if delta.seconds <= FLAGS.service_down_time:
73
 
            rv['compute'] = 'up'
74
 
        else:
75
 
            rv['compute'] = 'down'
76
 
    if volume_service:
77
 
        latest = volume_service['updated_at'] or volume_service['created_at']
78
 
        delta = now - latest
79
 
        if delta.seconds <= FLAGS.service_down_time:
80
 
            rv['volume'] = 'up'
81
 
        else:
82
 
            rv['volume'] = 'down'
83
 
    return rv
84
 
 
85
 
 
86
 
def instance_dict(inst):
87
 
    return {'name': inst['name'],
88
 
            'memory_mb': inst['memory_mb'],
89
 
            'vcpus': inst['vcpus'],
90
 
            'disk_gb': inst['local_gb'],
91
 
            'flavor_id': inst['flavorid']}
92
 
 
93
 
 
94
 
def vpn_dict(project, vpn_instance):
95
 
    rv = {'project_id': project.id,
96
 
          'public_ip': project.vpn_ip,
97
 
          'public_port': project.vpn_port}
98
 
    if vpn_instance:
99
 
        rv['instance_id'] = ec2utils.id_to_ec2_id(vpn_instance['id'])
100
 
        rv['created_at'] = utils.isotime(vpn_instance['created_at'])
101
 
        address = vpn_instance.get('fixed_ip', None)
102
 
        if address:
103
 
            rv['internal_ip'] = address['address']
104
 
        if project.vpn_ip and project.vpn_port:
105
 
            if utils.vpn_ping(project.vpn_ip, project.vpn_port):
106
 
                rv['state'] = 'running'
107
 
            else:
108
 
                rv['state'] = 'down'
109
 
        else:
110
 
            rv['state'] = 'down - invalid project vpn config'
 
55
    if host:
 
56
        return host.state
111
57
    else:
112
 
        rv['state'] = 'pending'
113
 
    return rv
 
58
        return {}
114
59
 
115
60
 
116
61
class AdminController(object):
121
66
    def __str__(self):
122
67
        return 'AdminController'
123
68
 
124
 
    def __init__(self):
125
 
        self.compute_api = compute.API()
126
 
 
127
 
    def describe_instance_types(self, context, **_kwargs):
128
 
        """Returns all active instance types data (vcpus, memory, etc.)"""
129
 
        return {'instanceTypeSet': [instance_dict(v) for v in
130
 
                                   db.instance_type_get_all(context).values()]}
131
 
 
132
69
    def describe_user(self, _context, name, **_kwargs):
133
70
        """Returns user data, including access and secret keys."""
134
71
        return user_dict(manager.AuthManager().get_user(name))
138
75
        return {'userSet':
139
76
                [user_dict(u) for u in manager.AuthManager().get_users()]}
140
77
 
141
 
    def register_user(self, context, name, **_kwargs):
 
78
    def register_user(self, _context, name, **_kwargs):
142
79
        """Creates a new user, and returns generated credentials."""
143
 
        LOG.audit(_("Creating new user: %s"), name, context=context)
144
80
        return user_dict(manager.AuthManager().create_user(name))
145
81
 
146
 
    def deregister_user(self, context, name, **_kwargs):
 
82
    def deregister_user(self, _context, name, **_kwargs):
147
83
        """Deletes a single user (NOT undoable.)
148
84
           Should throw an exception if the user has instances,
149
85
           volumes, or buckets remaining.
150
86
        """
151
 
        LOG.audit(_("Deleting user: %s"), name, context=context)
152
87
        manager.AuthManager().delete_user(name)
 
88
 
153
89
        return True
154
90
 
155
91
    def describe_roles(self, context, project_roles=True, **kwargs):
169
105
                         operation='add', **kwargs):
170
106
        """Add or remove a role for a user and project."""
171
107
        if operation == 'add':
172
 
            if project:
173
 
                msg = _("Adding role %(role)s to user %(user)s"
174
 
                        " for project %(project)s") % locals()
175
 
                LOG.audit(msg, context=context)
176
 
            else:
177
 
                msg = _("Adding sitewide role %(role)s to"
178
 
                        " user %(user)s") % locals()
179
 
                LOG.audit(msg, context=context)
180
108
            manager.AuthManager().add_role(user, role, project)
181
109
        elif operation == 'remove':
182
 
            if project:
183
 
                msg = _("Removing role %(role)s from user %(user)s"
184
 
                        " for project %(project)s") % locals()
185
 
                LOG.audit(msg, context=context)
186
 
            else:
187
 
                msg = _("Removing sitewide role %(role)s"
188
 
                        " from user %(user)s") % locals()
189
 
                LOG.audit(msg, context=context)
190
110
            manager.AuthManager().remove_role(user, role, project)
191
111
        else:
192
 
            raise exception.ApiError(_('operation must be add or remove'))
 
112
            raise exception.ApiError('operation must be add or remove')
193
113
 
194
114
        return True
195
115
 
196
 
    def generate_x509_for_user(self, context, name, project=None, **kwargs):
 
116
    def generate_x509_for_user(self, _context, name, project=None, **kwargs):
197
117
        """Generates and returns an x509 certificate for a single user.
198
118
           Is usually called from a client that will wrap this with
199
119
           access and secret key info, and return a zip file.
202
122
            project = name
203
123
        project = manager.AuthManager().get_project(project)
204
124
        user = manager.AuthManager().get_user(name)
205
 
        msg = _("Getting x509 for user: %(name)s"
206
 
                " on project: %(project)s") % locals()
207
 
        LOG.audit(msg, context=context)
208
125
        return user_dict(user, base64.b64encode(project.get_credentials(user)))
209
126
 
210
127
    def describe_project(self, context, name, **kwargs):
220
137
    def register_project(self, context, name, manager_user, description=None,
221
138
                         member_users=None, **kwargs):
222
139
        """Creates a new project"""
223
 
        msg = _("Create project %(name)s managed by"
224
 
                " %(manager_user)s") % locals()
225
 
        LOG.audit(msg, context=context)
226
140
        return project_dict(
227
141
            manager.AuthManager().create_project(
228
142
                name,
230
144
                description=None,
231
145
                member_users=None))
232
146
 
233
 
    def modify_project(self, context, name, manager_user, description=None,
234
 
                       **kwargs):
235
 
        """Modifies a project"""
236
 
        msg = _("Modify project: %(name)s managed by"
237
 
                " %(manager_user)s") % locals()
238
 
        LOG.audit(msg, context=context)
239
 
        manager.AuthManager().modify_project(name,
240
 
                                             manager_user=manager_user,
241
 
                                             description=description)
242
 
        return True
243
 
 
244
147
    def deregister_project(self, context, name):
245
148
        """Permanently deletes a project."""
246
 
        LOG.audit(_("Delete project: %s"), name, context=context)
247
149
        manager.AuthManager().delete_project(name)
248
150
        return True
249
151
 
257
159
                              **kwargs):
258
160
        """Add or remove a user from a project."""
259
161
        if operation == 'add':
260
 
            msg = _("Adding user %(user)s to project %(project)s") % locals()
261
 
            LOG.audit(msg, context=context)
262
162
            manager.AuthManager().add_to_project(user, project)
263
163
        elif operation == 'remove':
264
 
            msg = _("Removing user %(user)s from"
265
 
                    " project %(project)s") % locals()
266
 
            LOG.audit(msg, context=context)
267
164
            manager.AuthManager().remove_from_project(user, project)
268
165
        else:
269
 
            raise exception.ApiError(_('operation must be add or remove'))
 
166
            raise exception.ApiError('operation must be add or remove')
270
167
        return True
271
168
 
272
 
    def _vpn_for(self, context, project_id):
273
 
        """Get the VPN instance for a project ID."""
274
 
        for instance in db.instance_get_all_by_project(context, project_id):
275
 
            if (instance['image_id'] == str(FLAGS.vpn_image_id)
276
 
                and not instance['vm_state'] in [vm_states.DELETED]):
277
 
                return instance
278
 
 
279
 
    def start_vpn(self, context, project):
280
 
        instance = self._vpn_for(context, project)
281
 
        if not instance:
282
 
            # NOTE(vish) import delayed because of __init__.py
283
 
            from nova.cloudpipe import pipelib
284
 
            pipe = pipelib.CloudPipe()
285
 
            proj = manager.AuthManager().get_project(project)
286
 
            user_id = proj.project_manager_id
287
 
            try:
288
 
                pipe.launch_vpn_instance(project, user_id)
289
 
            except db.NoMoreNetworks:
290
 
                raise exception.ApiError("Unable to claim IP for VPN instance"
291
 
                                         ", ensure it isn't running, and try "
292
 
                                         "again in a few minutes")
293
 
            instance = self._vpn_for(context, project)
294
 
        return {'instance_id': ec2utils.id_to_ec2_id(instance['id'])}
295
 
 
296
 
    def describe_vpns(self, context):
297
 
        vpns = []
298
 
        for project in manager.AuthManager().get_projects():
299
 
            instance = self._vpn_for(context, project.id)
300
 
            vpns.append(vpn_dict(project, instance))
301
 
        return {'items': vpns}
302
 
 
303
169
    # FIXME(vish): these host commands don't work yet, perhaps some of the
304
170
    #              required data can be retrieved from service objects?
305
 
 
306
 
    def describe_hosts(self, context, **_kwargs):
 
171
    def describe_hosts(self, _context, **_kwargs):
307
172
        """Returns status info for all nodes. Includes:
308
 
            * Hostname
309
 
            * Compute (up, down, None)
310
 
            * Instance count
311
 
            * Volume (up, down, None)
312
 
            * Volume Count
 
173
            * Disk Space
 
174
            * Instance List
 
175
            * RAM used
 
176
            * CPU used
 
177
            * DHCP servers running
 
178
            * Iptables / bridges
313
179
        """
314
 
        services = db.service_get_all(context, False)
315
 
        now = utils.utcnow()
316
 
        hosts = []
317
 
        rv = []
318
 
        for host in [service['host'] for service in services]:
319
 
            if not host in hosts:
320
 
                hosts.append(host)
321
 
        for host in hosts:
322
 
            compute = [s for s in services if s['host'] == host \
323
 
                                           and s['binary'] == 'nova-compute']
324
 
            if compute:
325
 
                compute = compute[0]
326
 
            instances = db.instance_get_all_by_host(context, host)
327
 
            volume = [s for s in services if s['host'] == host \
328
 
                                           and s['binary'] == 'nova-volume']
329
 
            if volume:
330
 
                volume = volume[0]
331
 
            volumes = db.volume_get_all_by_host(context, host)
332
 
            rv.append(host_dict(host, compute, instances, volume, volumes,
333
 
                                now))
334
 
        return {'hosts': rv}
335
 
 
336
 
    def _provider_fw_rule_exists(self, context, rule):
337
 
        # TODO(todd): we call this repeatedly, can we filter by protocol?
338
 
        for old_rule in db.provider_fw_rule_get_all(context):
339
 
            if all([rule[k] == old_rule[k] for k in ('cidr', 'from_port',
340
 
                                                     'to_port', 'protocol')]):
341
 
                return True
342
 
        return False
343
 
 
344
 
    def block_external_addresses(self, context, cidr):
345
 
        """Add provider-level firewall rules to block incoming traffic."""
346
 
        LOG.audit(_('Blocking traffic to all projects incoming from %s'),
347
 
                  cidr, context=context)
348
 
        cidr = urllib.unquote(cidr).decode()
349
 
        # raise if invalid
350
 
        netaddr.IPNetwork(cidr)
351
 
        rule = {'cidr': cidr}
352
 
        tcp_rule = rule.copy()
353
 
        tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535})
354
 
        udp_rule = rule.copy()
355
 
        udp_rule.update({'protocol': 'udp', 'from_port': 1, 'to_port': 65535})
356
 
        icmp_rule = rule.copy()
357
 
        icmp_rule.update({'protocol': 'icmp', 'from_port': -1,
358
 
                          'to_port': None})
359
 
        rules_added = 0
360
 
        if not self._provider_fw_rule_exists(context, tcp_rule):
361
 
            db.provider_fw_rule_create(context, tcp_rule)
362
 
            rules_added += 1
363
 
        if not self._provider_fw_rule_exists(context, udp_rule):
364
 
            db.provider_fw_rule_create(context, udp_rule)
365
 
            rules_added += 1
366
 
        if not self._provider_fw_rule_exists(context, icmp_rule):
367
 
            db.provider_fw_rule_create(context, icmp_rule)
368
 
            rules_added += 1
369
 
        if not rules_added:
370
 
            raise exception.ApiError(_('Duplicate rule'))
371
 
        self.compute_api.trigger_provider_fw_rules_refresh(context)
372
 
        return {'status': 'OK', 'message': 'Added %s rules' % rules_added}
373
 
 
374
 
    def describe_external_address_blocks(self, context):
375
 
        blocks = db.provider_fw_rule_get_all(context)
376
 
        # NOTE(todd): use a set since we have icmp/udp/tcp rules with same cidr
377
 
        blocks = set([b.cidr for b in blocks])
378
 
        blocks = [{'cidr': b} for b in blocks]
379
 
        return {'externalIpBlockInfo':
380
 
                list(sorted(blocks, key=lambda k: k['cidr']))}
381
 
 
382
 
    def remove_external_address_block(self, context, cidr):
383
 
        LOG.audit(_('Removing ip block from %s'), cidr, context=context)
384
 
        cidr = urllib.unquote(cidr).decode()
385
 
        # raise if invalid
386
 
        netaddr.IPNetwork(cidr)
387
 
        rules = db.provider_fw_rule_get_all_by_cidr(context, cidr)
388
 
        for rule in rules:
389
 
            db.provider_fw_rule_destroy(context, rule['id'])
390
 
        if rules:
391
 
            self.compute_api.trigger_provider_fw_rules_refresh(context)
392
 
        return {'status': 'OK', 'message': 'Deleted %s rules' % len(rules)}
 
180
        return {'hostSet': [host_dict(h) for h in db.host_get_all()]}
 
181
 
 
182
    def describe_host(self, _context, name, **_kwargs):
 
183
        """Returns status info for single node."""
 
184
        return host_dict(db.host_get(name))