def attach_volume(self, context, volume_id, instance_id, device, **kwargs): volume = self._get_volume(context, volume_id) if volume['status'] == "attached": raise exception.ApiError("Volume is already attached") # TODO(vish): looping through all volumes is slow. We should probably maintain an index for vol in self.volumes: if vol['instance_id'] == instance_id and vol[ 'mountpoint'] == device: raise exception.ApiError( "Volume %s is already attached to %s" % (vol['volume_id'], vol['mountpoint'])) volume.start_attach(instance_id, device) instance = self._get_instance(context, instance_id) compute_node = instance['node_name'] rpc.cast( '%s.%s' % (FLAGS.compute_topic, compute_node), { "method": "attach_volume", "args": { "volume_id": volume_id, "instance_id": instance_id, "mountpoint": device } }) return defer.succeed({ 'attachTime': volume['attach_time'], 'device': volume['mountpoint'], 'instanceId': instance_id, 'requestId': context.request_id, 'status': volume['attach_status'], 'volumeId': volume_id })
def authorize_security_group_ingress(self, context, group_name, **kwargs): LOG.audit(_("Authorize security group ingress %s"), group_name, context=context) self.compute_api.ensure_default_security_group(context) security_group = db.security_group_get_by_name(context, context.project_id, group_name) values = self._revoke_rule_args_to_dict(context, **kwargs) if values is None: raise exception.ApiError( _("Not enough parameters to build a " "valid rule.")) values['parent_group_id'] = security_group.id if self._security_group_rule_exists(security_group, values): raise exception.ApiError( _('This rule already exists in group %s') % group_name) security_group_rule = db.security_group_rule_create(context, values) self.compute_api.trigger_security_group_rules_refresh( context, security_group['id']) return True
def attach_volume(self, context, volume_id, instance_id, device, **kwargs): storage_node, volume = self._get_volume(volume_id) # TODO: (joshua) Fix volumes to store creator id if not context.user.is_authorized(volume.get('user_id', None)): raise exception.ApiError("%s not authorized for %s" % (context.user.id, volume_id)) compute_node, instance = self._get_instance(instance_id) if not context.user.is_authorized(instance.get('owner_id', None)): raise exception.ApiError(message="%s not authorized for %s" % (context.user.id, instance_id)) aoe_device = volume['aoe_device'] # Needs to get right node controller for attaching to # TODO: Maybe have another exchange that goes to everyone? rpc.cast( '%s.%s' % (FLAGS.compute_topic, compute_node), { "method": "attach_volume", "args": { "aoe_device": aoe_device, "instance_id": instance_id, "mountpoint": device } }) rpc.cast( '%s.%s' % (FLAGS.storage_topic, storage_node), { "method": "attach_volume", "args": { "volume_id": volume_id, "instance_id": instance_id, "mountpoint": device } }) return defer.succeed(True)
def revoke_security_group_ingress(self, context, group_name, **kwargs): LOG.audit(_("Revoke security group ingress %s"), group_name, context=context) self.compute_api.ensure_default_security_group(context) security_group = db.security_group_get_by_name(context, context.project_id, group_name) criteria = self._revoke_rule_args_to_dict(context, **kwargs) if criteria is None: raise exception.ApiError( _("Not enough parameters to build a " "valid rule.")) for rule in security_group.rules: match = True for (k, v) in criteria.iteritems(): if getattr(rule, k, False) != v: match = False if match: db.security_group_rule_destroy(context, rule['id']) self.compute_api.trigger_security_group_rules_refresh( context, security_group['id']) return True raise exception.ApiError(_("No rule for the specified parameters."))
def detach_volume(self, context, volume_id, **kwargs): # TODO(joshua): Make sure the updated state has been received first storage_node, volume = self._get_volume(volume_id) if not context.user.is_authorized(volume.get('user_id', None)): raise exception.ApiError("%s not authorized for %s" % (context.user.id, volume_id)) instance = None if volume.has_key('instance_id'): instance_id = volume['instance_id'] try: compute_node, instance = self._get_instance(instance_id) mountpoint = volume['mountpoint'] if not context.user.is_authorized( instance.get('owner_id', None)): raise exception.ApiError("%s not authorized for %s" % (context.user.id, instance_id)) rpc.cast( '%s.%s' % (FLAGS.compute_topic, compute_node), { "method": "detach_volume", "args": { "instance_id": instance_id, "mountpoint": mountpoint } }) except exception.NotFound: pass rpc.cast('%s.%s' % (FLAGS.storage_topic, storage_node), { "method": "detach_volume", "args": { "volume_id": volume_id } }) return defer.succeed(True)
def create(self, req, body): context = req.environ['nova.context'] group_id = body['security_group_rule']['parent_group_id'] self.compute_api.ensure_default_security_group(context) security_group = db.security_group_get(context, group_id) if not security_group: raise exception.SecurityGroupNotFound(security_group_id=group_id) msg = "Authorize security group ingress %s" LOG.audit(_(msg), security_group['name'], context=context) values = self._revoke_rule_args_to_dict(context, **body['security_group_rule']) if values is None: raise exception.ApiError(_("Not enough parameters to build a " "valid rule.")) values['parent_group_id'] = security_group.id if self._security_group_rule_exists(security_group, values): raise exception.ApiError(_('This rule already exists in group %s') % group_id) security_group_rule = db.security_group_rule_create(context, values) self.compute_api.trigger_security_group_rules_refresh(context, security_group_id=security_group['id']) return {'security_group_rule': self._format_security_group_rule( context, security_group_rule)}
def check_attach(self, context, volume_id): volume = self.get(context, volume_id) # TODO(vish): abstract status checking? if volume['status'] != "available": raise exception.ApiError(_("Volume status must be available")) if volume['attach_status'] == "attached": raise exception.ApiError(_("Volume is already attached"))
def _check_storage_parameters(self, context, vsa_name, storage, shared, first_index=0): """ Translates storage array of disks to the list of volumes :param storage: List of dictionaries with following keys: disk_name, num_disks, size :param shared: Specifies if storage is dedicated or shared. For shared storage disks split into partitions """ volume_params = [] for node in storage: name = node.get('drive_name', None) num_disks = node.get('num_drives', 1) if name is None: raise exception.ApiError( _("No drive_name param found in %s") % node) try: vol_type = volume_types.get_volume_type_by_name(context, name) except exception.NotFound: raise exception.ApiError( _("Invalid drive type name %s") % name) self._check_volume_type_correctness(vol_type) # if size field present - override disk size specified in DB size = int( node.get('size', vol_type['extra_specs'].get('drive_size'))) if shared: part_size = FLAGS.vsa_part_size_gb total_capacity = num_disks * size num_volumes = total_capacity / part_size size = part_size else: num_volumes = num_disks size = 0 # special handling for full drives for i in range(num_volumes): volume_name = "drive-%03d" % first_index first_index += 1 volume_desc = 'BE volume for VSA %s type %s' % \ (vsa_name, name) volume = { 'size': size, 'name': volume_name, 'description': volume_desc, 'volume_type_id': vol_type['id'], } volume_params.append(volume) return volume_params
def modify_image_attribute(self, context, image_id, attribute, operation_type, **kwargs): if attribute != 'launchPermission': raise exception.ApiError('only launchPermission is supported') if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all': raise exception.ApiError('only group "all" is supported') if not operation_type in ['add', 'delete']: raise exception.ApiError('operation_type must be add or delete') result = images.modify(context.user, image_id, operation_type) return defer.succeed(result)
def test_return_valid_error(self): # without 'code' arg err = exception.ApiError('fake error') self.assertEqual(err.__str__(), 'fake error') self.assertEqual(err.code, None) self.assertEqual(err.msg, 'fake error') # with 'code' arg err = exception.ApiError('fake error', 'blah code') self.assertEqual(err.__str__(), 'blah code: fake error') self.assertEqual(err.code, 'blah code') self.assertEqual(err.msg, 'fake error')
def modify_image_attribute(self, context, image_id, attribute, operation_type, **kwargs): # TODO(devcamcar): Support users and groups other than 'all'. if attribute != 'launchPermission': raise exception.ApiError('attribute not supported: %s' % attribute) if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all': raise exception.ApiError('only group "all" is supported') if not operation_type in ['add', 'delete']: raise exception.ApiError('operation_type must be add or delete') result = images.modify(context, image_id, operation_type) return defer.succeed(result)
def associate_floating_ip(self, context, floating_ip, fixed_ip, affect_auto_assigned=False): """Associates a floating ip with a fixed ip. ensures floating ip is allocated to the project in context :param fixed_ip: is either fixed_ip object or a string fixed ip address :param floating_ip: is a string floating ip address """ # NOTE(tr3buchet): i don't like the "either or" argument type # funcationility but i've left it alone for now # TODO(tr3buchet): this function needs to be rewritten to move # the network related db lookups into the network host code if isinstance(fixed_ip, basestring): fixed_ip = self.db.fixed_ip_get_by_address(context, fixed_ip) floating_ip = self.db.floating_ip_get_by_address(context, floating_ip) if not affect_auto_assigned and floating_ip.get('auto_assigned'): return # Check if the floating ip address is allocated if floating_ip['project_id'] is None: raise exception.ApiError( _('Address (%s) is not allocated') % floating_ip['address']) # Check if the floating ip address is allocated to the same project if floating_ip['project_id'] != context.project_id: LOG.warn( _('Address (%(address)s) is not allocated to your ' 'project (%(project)s)'), { 'address': floating_ip['address'], 'project': context.project_id }) raise exception.ApiError( _('Address (%(address)s) is not ' 'allocated to your project' '(%(project)s)') % { 'address': floating_ip['address'], 'project': context.project_id }) # NOTE(vish): if we are multi_host, send to the instances host if fixed_ip['network']['multi_host']: host = fixed_ip['instance']['host'] else: host = fixed_ip['network']['host'] rpc.cast( context, self.db.queue_get_for(context, FLAGS.network_topic, host), { 'method': 'associate_floating_ip', 'args': { 'floating_address': floating_ip['address'], 'fixed_address': fixed_ip['address'] } })
def describe_image_attribute(self, context, image_id, attribute, **kwargs): if attribute != 'launchPermission': raise exception.ApiError('attribute not supported: %s' % attribute) try: image = images.list(context, image_id)[0] except IndexError: raise exception.ApiError('invalid id: %s' % image_id) result = {'image_id': image_id, 'launchPermission': []} if image['isPublic']: result['launchPermission'].append({'group': 'all'}) return defer.succeed(result)
def get_console_output(self, context, instance_id, **kwargs): # instance_id is passed in as a list of instances node, instance = self._get_instance(instance_id[0]) if node == 'pending': raise exception.ApiError('Cannot get output for pending instance') if not context.user.is_authorized(instance.get('owner_id', None)): raise exception.ApiError('Not authorized to view output') return rpc.call( '%s.%s' % (FLAGS.compute_topic, node), { "method": "get_console_output", "args": { "instance_id": instance_id[0] } })
def _create_snapshot(self, context, volume, name, description, force=False): check_policy(context, 'create_snapshot', volume) if ((not force) and (volume['status'] != "available")): raise exception.ApiError(_("Volume status must be available")) options = { 'volume_id': volume['id'], 'user_id': context.user_id, 'project_id': context.project_id, 'status': "creating", 'progress': '0%', 'volume_size': volume['size'], 'display_name': name, 'display_description': description } snapshot = self.db.snapshot_create(context, options) rpc.cast( context, FLAGS.scheduler_topic, { "method": "create_snapshot", "args": { "topic": FLAGS.volume_topic, "volume_id": volume['id'], "snapshot_id": snapshot['id'] } }) return snapshot
def run_instances(self, context, **kwargs): # make sure user can access the image # vpn image is private so it doesn't show up on lists if kwargs['image_id'] != FLAGS.vpn_image_id: image = self._get_image(context, kwargs['image_id']) logging.debug("Going to run instances...") reservation_id = utils.generate_uid('r') launch_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) key_data = None if kwargs.has_key('key_name'): key_pair = context.user.get_key_pair(kwargs['key_name']) if not key_pair: raise exception.ApiError('Key Pair %s not found' % kwargs['key_name']) key_data = key_pair.public_key # TODO: Get the real security group of launch in here security_group = "default" bridge_name = network.BridgedNetwork.get_network_for_project( context.user.id, context.project.id, security_group)['bridge_name'] for num in range(int(kwargs['max_count'])): inst = self.instdir.new() # TODO(ja): add ari, aki inst['image_id'] = kwargs['image_id'] if 'kernel_id' in kwargs: inst['kernel_id'] = kwargs['kernel_id'] if 'ramdisk_id' in kwargs: inst['ramdisk_id'] = kwargs['ramdisk_id'] inst['user_data'] = kwargs.get('user_data', '') inst['instance_type'] = kwargs.get('instance_type', 'm1.small') inst['reservation_id'] = reservation_id inst['launch_time'] = launch_time inst['key_data'] = key_data or '' inst['key_name'] = kwargs.get('key_name', '') inst['user_id'] = context.user.id inst['project_id'] = context.project.id inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = num inst['bridge_name'] = bridge_name if inst['image_id'] == FLAGS.vpn_image_id: address = network.allocate_vpn_ip(inst['user_id'], inst['project_id'], mac=inst['mac_address']) else: address = network.allocate_ip(inst['user_id'], inst['project_id'], mac=inst['mac_address']) inst['private_dns_name'] = str(address) # TODO: allocate expresses on the router node inst.save() rpc.cast( FLAGS.compute_topic, { "method": "run_instance", "args": { "instance_id": inst.instance_id } }) logging.debug("Casting to node for %s's instance with IP of %s" % (context.user.name, inst['private_dns_name'])) # TODO: Make the NetworkComputeNode figure out the network name from ip. return defer.succeed(self._format_instances(context, reservation_id))
def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" LOG.audit(_('Blocking traffic to all projects incoming from %s'), cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid netaddr.IPNetwork(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535}) udp_rule = rule.copy() udp_rule.update({'protocol': 'udp', 'from_port': 1, 'to_port': 65535}) icmp_rule = rule.copy() icmp_rule.update({ 'protocol': 'icmp', 'from_port': -1, 'to_port': None }) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): db.provider_fw_rule_create(context, tcp_rule) rules_added += 1 if not self._provider_fw_rule_exists(context, udp_rule): db.provider_fw_rule_create(context, udp_rule) rules_added += 1 if not self._provider_fw_rule_exists(context, icmp_rule): db.provider_fw_rule_create(context, icmp_rule) rules_added += 1 if not rules_added: raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) return {'status': 'OK', 'message': 'Added %s rules' % rules_added}
def create(name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types.""" for option in [memory, vcpus, local_gb, flavorid]: try: int(option) except ValueError: raise exception.InvalidInput(reason=_("create arguments must " "be positive integers")) if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): raise exception.InvalidInput(reason=_("create arguments must " "be positive integers")) try: db.instance_type_create( context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid, swap=swap, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) except exception.DBError, e: LOG.exception(_('DB error: %s') % e) raise exception.ApiError( _("Cannot create instance_type with " "name %(name)s and flavorid %(flavorid)s") % locals())
def modify_user_role(self, context, user, role, project=None, operation='add', **kwargs): """Add or remove a role for a user and project.""" if operation == 'add': if project: msg = _("Adding role %(role)s to user %(user)s" " for project %(project)s") % locals() LOG.audit(msg, context=context) else: msg = _("Adding sitewide role %(role)s to" " user %(user)s") % locals() LOG.audit(msg, context=context) manager.AuthManager().add_role(user, role, project) elif operation == 'remove': if project: msg = _("Removing role %(role)s from user %(user)s" " for project %(project)s") % locals() LOG.audit(msg, context=context) else: msg = _("Removing sitewide role %(role)s" " from user %(user)s") % locals() LOG.audit(msg, context=context) manager.AuthManager().remove_role(user, role, project) else: raise exception.ApiError(_('operation must be add or remove')) return True
def create(name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=0, rxtx_cap=0): """Creates instance types / flavors arguments: name memory vcpus local_gb flavorid swap rxtx_quota rxtx_cap """ for option in [memory, vcpus, local_gb, flavorid]: try: int(option) except ValueError: raise exception.InvalidInputException( _("create arguments must be positive integers")) if (int(memory) <= 0) or (int(vcpus) <= 0) or (int(local_gb) < 0): raise exception.InvalidInputException( _("create arguments must be positive integers")) try: db.instance_type_create( context.get_admin_context(), dict(name=name, memory_mb=memory, vcpus=vcpus, local_gb=local_gb, flavorid=flavorid, swap=swap, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) except exception.DBError, e: LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Cannot create instance type: %s" % name))
def get_default_instance_type(): """Get the default instance type.""" name = FLAGS.default_instance_type try: return get_instance_type_by_name(name) except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_instance_type_by_flavor_id(flavorid): """Retrieve instance type by flavorid.""" ctxt = context.get_admin_context() try: return db.instance_type_get_by_flavor_id(ctxt, flavorid) except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s") % flavorid)
def create(self, context, size, snapshot_id, name, description, volume_type=None, metadata=None, availability_zone=None): if snapshot_id is not None: snapshot = self.get_snapshot(context, snapshot_id) if snapshot['status'] != "available": raise exception.ApiError( _("Snapshot status must be available")) if not size: size = snapshot['volume_size'] if quota.allowed_volumes(context, 1, size) < 1: pid = context.project_id LOG.warn( _("Quota exceeded for %(pid)s, tried to create" " %(size)sG volume") % locals()) raise exception.QuotaError( _("Volume quota exceeded. You cannot " "create a volume of size %sG") % size) if availability_zone is None: availability_zone = FLAGS.storage_availability_zone if volume_type is None: volume_type_id = None else: volume_type_id = volume_type.get('id', None) options = { 'size': size, 'user_id': context.user_id, 'project_id': context.project_id, 'snapshot_id': snapshot_id, 'availability_zone': availability_zone, 'status': "creating", 'attach_status': "detached", 'display_name': name, 'display_description': description, 'volume_type_id': volume_type_id, 'metadata': metadata, } volume = self.db.volume_create(context, options) rpc.cast( context, FLAGS.scheduler_topic, { "method": "create_volume", "args": { "topic": FLAGS.volume_topic, "volume_id": volume['id'], "snapshot_id": snapshot_id } }) return volume
def _check_volume_type_correctness(self, vol_type): if vol_type.get('extra_specs') is None or\ vol_type['extra_specs'].get('type') != 'vsa_drive' or\ vol_type['extra_specs'].get('drive_type') is None or\ vol_type['extra_specs'].get('drive_size') is None: raise exception.ApiError( _("Invalid drive type %s") % vol_type['name'])
def get_instance_type(id): """Retrieves single instance type by id.""" if id is None: return get_default_instance_type() try: ctxt = context.get_admin_context() return db.instance_type_get(ctxt, id) except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s") % id)
def get_instance_type_by_name(name): """Retrieves single instance type by name.""" if name is None: return get_default_instance_type() try: ctxt = context.get_admin_context() return db.instance_type_get_by_name(ctxt, name) except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_volume_type_by_name(context, name): """Retrieves single volume type by name.""" if name is None: raise exception.InvalidVolumeType(volume_type=name) try: return db.volume_type_get_by_name(context, name) except exception.DBError: raise exception.ApiError(_("Unknown volume type: %s") % name)
def purge(name): """Removes instance types from database.""" if name is None: raise exception.InvalidInstanceType(instance_type=name) else: try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: LOG.exception(_('Instance type %s not found for purge') % name) raise exception.ApiError(_("Unknown instance type: %s") % name)
def destroy(name): """Marks instance types as deleted.""" if name is None: raise exception.InvalidInstanceType(instance_type=name) else: try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: LOG.exception(_('Instance type %s not found for deletion') % name) raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_instance_type(name): """Retrieves single instance type by name""" if name is None: return FLAGS.default_instance_type try: ctxt = context.get_admin_context() inst_type = db.instance_type_get_by_name(ctxt, name) return inst_type except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s" % name))