def on_post(self, req, resp, tenant_id): body = json.loads(req.stream.read().decode()) try: name = body['keypair']['name'] key = body['keypair'].get('public_key', generate_random_key()) except (KeyError, TypeError): return error_handling.bad_request( resp, 'Not all fields exist to create keypair.') validate_result = validate_keypair_name(resp, name) if not validate_result: return client = req.sl_client mgr = SoftLayer.SshKeyManager(client) # Make sure the key with that label doesn't already exist existing_keys = mgr.list_keys(label=name) if existing_keys: return error_handling.duplicate(resp, 'Duplicate key by that name') try: keypair = mgr.add_key(key, name) resp.body = {'keypair': format_keypair(keypair)} except SoftLayer.SoftLayerAPIError as e: if 'Unable to generate a fingerprint' in e.faultString: return error_handling.bad_request(resp, e.faultString) if 'SSH key already exists' in e.faultString: return error_handling.duplicate(resp, e.faultString) raise
def on_post(self, req, resp, tenant_id): body = json.loads(req.stream.read().decode()) try: name = body['keypair']['name'] key = body['keypair'].get('public_key', generate_random_key()) except (KeyError, TypeError): return error_handling.bad_request( resp, 'Not all fields exist to create keypair.') validate_result = validate_keypair_name(resp, name) if not validate_result: return client = req.env['sl_client'] mgr = SoftLayer.SshKeyManager(client) # Make sure the key with that label doesn't already exist existing_keys = mgr.list_keys(label=name) if existing_keys: return error_handling.duplicate(resp, 'Duplicate key by that name') try: keypair = mgr.add_key(key, name) resp.body = {'keypair': format_keypair(keypair)} except SoftLayer.SoftLayerAPIError as e: if 'Unable to generate a fingerprint' in e.faultString: return error_handling.bad_request(resp, e.faultString) if 'SSH key already exists' in e.faultString: return error_handling.duplicate(resp, e.faultString) raise
def validate_keypair_name(resp, key_name): safechars = "_- " + string.digits + string.ascii_letters clean_value = "".join(x for x in key_name if x in safechars) if clean_value != key_name: error_handling.bad_request( resp, 'Keypair name contains unsafe characters') return False if not 0 < len(key_name) < 256: error_handling.bad_request( resp, 'Keypair name must be between 1 and 255 characters long') return False return True
def on_get(self, req, resp, tenant_id, flavor_id): '''Returns the extra specs for a particular flavor ''' for flavor in self.flavors: if str(flavor_id) == flavor['id']: extra_specs = flavor['extra_specs'] resp.status = 200 resp.body = {'extra_specs': extra_specs} return else: error_handling.bad_request(resp, message="Invalid Flavor ID " "requested.") return
def on_post(self, req, resp, tenant_id=None): body = json.loads(req.stream.read().decode()) image_id = body.get('id', str(uuid.uuid4())) url = body.get('direct_url') osRefCode = body.get('os_version', None) if not all([url, osRefCode]): raise bad_request(resp, "Swift url and OS code must be given") configuration = { 'name': body.get('name'), 'note': '', 'operatingSystemReferenceCode': osRefCode, 'uri': url } image_service = req.env['sl_client'][ 'SoftLayer_Virtual_Guest_Block_Device_Template_Group'] img = image_service.createFromExternalSource(configuration) resp.body = { 'id': img['globalIdentifier'], 'name': body['name'], 'status': 'queued', 'visibility': 'private', 'tags': [], 'created_at': img['createDate'], 'updated_at': img['createDate'], 'self': self.app.get_endpoint_url( 'image', req, 'v2_image', image_guid=image_id), 'file': self.app.get_endpoint_url( 'image', req, 'v2_image_file', image_guid=image_id), 'schema': self.app.get_endpoint_url('image', req, 'v2_schema_image'), }
def on_post(self, req, resp, tenant_id): """Create volume (SL Portable storage).""" client = req.env['sl_client'] try: body = json.loads(req.stream.read().decode()) # required field in the create volume payload namestr = body['volume'].get("display_name") volreq = body['volume'] # portable storage order cannot have empty name name = (config.CONF['volume']['volume_name_prefix'] + (namestr if namestr else "")) # size is required option for volume create. Throw type exception # if it is invalid size = int(volreq['size']) # availability_zone is optional, don't throw exception if # it is not available availability_zone = ( body['volume'].get('availability_zone') or config.CONF['volume']['default_availability_zone']) volume_type = body['volume'].get('volume_type') except Exception: return error_handling.bad_request(resp, 'Malformed request body') try: volinfo = self._create_volume(tenant_id, client, resp, size, name=name, zone=availability_zone, volume_type=volume_type) resp.status = HTTP.ACCEPTED if volinfo: resp.body = {'volume': format_volume(tenant_id, volinfo, client)} resp.body['volume'].update({'status': 'creating'}) else: # Cannot generate a valid response without knowning # the volume id when order takes too long to complete. # This should be a rare case, but it could break openstack # since the volume create caller always expect a volume id # uppon successful return. The approach here is to fail # the volume create operation and leak one portable storage # volume. User can always cancel from SL portal. return error_handling.volume_fault( resp, "Portable storage order delayed") except SoftLayer.SoftLayerAPIError as e: return error_handling.error(resp, "SoftLayerAPIError", e.faultString, code=HTTP.INTERNAL_SERVER_ERROR) except Exception as e: return error_handling.volume_fault(resp, str(e))
def on_get(self, req, resp, tenant_id, flavor_id, key_id): '''Returns the requested key from the optional extra specs ''' for flavor in self.flavors: if str(flavor_id) == flavor['id']: extra_specs = flavor['extra_specs'] if key_id in extra_specs: resp.status = 200 resp.body = {key_id: extra_specs[key_id]} return else: error_handling.bad_request(resp, message="Invalid Key ID " "requested") return else: error_handling.bad_request(resp, message="Invalid Flavor ID " "requested.") return
def on_post(self, req, resp, tenant_id): payload = {} client = req.env['sl_client'] body = json.loads(req.stream.read().decode()) payload['hostname'] = body['server']['name'] payload['domain'] = config.CONF['default_domain'] or 'jumpgate.com' payload['image_id'] = body['server']['imageRef'] # TODO(kmcdonald) - How do we set this accurately? payload['hourly'] = True networks = utils.lookup(body, 'server', 'networks') cci = SoftLayer.CCIManager(client) try: self._handle_flavor(payload, body) self._handle_sshkeys(payload, body, client) self._handle_user_data(payload, body) self._handle_datacenter(payload, body) if networks: self._handle_network(payload, client, networks) new_instance = cci.create_instance(**payload) except Exception as e: return error_handling.bad_request(resp, message=str(e)) # This should be the first tag that the VS set. Adding any more tags # will replace this tag try: flavor_id = int(body['server'].get('flavorRef')) vs = client['Virtual_Guest'] vs.setTags('{"flavor_id": ' + str(flavor_id) + '}', id=new_instance['id']) except Exception: pass resp.set_header('x-compute-request-id', 'create') resp.status = 202 resp.body = { 'server': { 'id': new_instance['id'], 'links': [{ 'href': self.app.get_endpoint_url('compute', req, 'v2_server', instance_id=new_instance['id']), 'rel': 'self' }], 'adminPass': '', } }
def on_get(self, req, resp, tenant_id, volume_id): client = req.env['sl_client'] if volume_id and len(volume_id) <= OPENSTACK_VOLUME_UUID_LEN: # show volume details by volume id # /v1/{tenant_id}/volumes/{volume_id} self._show_volume(tenant_id, volume_id, client, req, resp) else: return error_handling.bad_request(resp, message="Malformed request body")
def on_delete(self, req, resp, tenant_id, volume_id): client = req.env['sl_client'] if volume_id and len(volume_id) <= OPENSTACK_VOLUME_UUID_LEN: # show volume details by volume id # /v1/{tenant_id}/volumes/{volume_id} self._delete_volume(tenant_id, volume_id, client, req, resp) else: return error_handling.bad_request(resp, message="Invalid volume Id")
def filter_flavor_refs(req, resp, flavor_refs): if req.get_param("marker") is not None: marker = req.get_param("marker") flavor_refs = [f for f in flavor_refs if str(f["id"]) > marker] if req.get_param("minDisk") is not None: try: min_disk = int(req.get_param("minDisk")) flavor_refs = [f for f in flavor_refs if f["disk"] >= min_disk] except ValueError: bad_request(resp, message="Invalid minDisk parameter.") return if req.get_param("minRam") is not None: try: min_ram = int(req.get_param("minRam")) flavor_refs = [f for f in flavor_refs if f["ram"] >= min_ram] except ValueError: bad_request(resp, message="Invalid minRam parameter.") return if req.get_param("limit") is not None: try: limit = int(req.get_param("limit")) flavor_refs = flavor_refs[:limit] except ValueError: bad_request(resp, message="Invalid limit parameter.") return return flavor_refs
def filter_flavor_refs(req, resp, flavor_refs): if req.get_param('marker') is not None: marker = req.get_param('marker') flavor_refs = [f for f in flavor_refs if str(f['id']) > marker] if req.get_param('minDisk') is not None: try: min_disk = int(req.get_param('minDisk')) flavor_refs = [f for f in flavor_refs if f['disk'] >= min_disk] except ValueError: error_handling.bad_request(resp, message="Invalid minDisk parameter.") return if req.get_param('minRam') is not None: try: min_ram = int(req.get_param('minRam')) flavor_refs = [f for f in flavor_refs if f['ram'] >= min_ram] except ValueError: error_handling.bad_request(resp, message="Invalid minRam parameter.") return if req.get_param('limit') is not None: try: limit = int(req.get_param('limit')) flavor_refs = flavor_refs[:limit] except ValueError: error_handling.bad_request(resp, message="Invalid limit parameter.") return return flavor_refs
def on_delete(self, req, resp, tenant_id, server_id): client = req.env["sl_client"] cci = CCIManager(client) try: cci.cancel_instance(server_id) except SoftLayerAPIError as e: if "active transaction" in e.faultString: return bad_request( resp, message="Can not cancel an instance when there is already" " an active transaction", code=409 ) raise resp.status = 204
def on_delete(self, req, resp, tenant_id, server_id): client = req.env['sl_client'] cci = SoftLayer.CCIManager(client) try: cci.cancel_instance(server_id) except SoftLayer.SoftLayerAPIError as e: if 'active transaction' in e.faultString: return error_handling.bad_request( resp, message='Can not cancel an instance when there is already' ' an active transaction', code=409) raise resp.status = 204
def on_post(self, req, resp, tenant_id): payload = {} client = req.env['sl_client'] body = json.loads(req.stream.read().decode()) payload['hostname'] = body['server']['name'] payload['domain'] = config.CONF['default_domain'] or 'jumpgate.com' payload['image_id'] = body['server']['imageRef'] # TODO(kmcdonald) - How do we set this accurately? payload['hourly'] = True networks = utils.lookup(body, 'server', 'networks') cci = SoftLayer.CCIManager(client) try: self._handle_flavor(payload, body) self._handle_sshkeys(payload, body, client) self._handle_user_data(payload, body) self._handle_datacenter(payload, body) if networks: self._handle_network(payload, client, networks) new_instance = cci.create_instance(**payload) except Exception as e: return error_handling.bad_request(resp, message=str(e)) # This should be the first tag that the VS set. Adding any more tags # will replace this tag try: flavor_id = int(body['server'].get('flavorRef')) vs = client['Virtual_Guest'] vs.setTags('{"flavor_id": ' + str(flavor_id) + '}', id=new_instance['id']) except Exception: pass resp.set_header('x-compute-request-id', 'create') resp.status = 202 resp.body = {'server': { # Casted to string to make tempest pass 'id': str(new_instance['id']), 'links': [{ 'href': self.app.get_endpoint_url( 'compute', req, 'v2_server', instance_id=new_instance['id']), 'rel': 'self'}], 'adminPass': '', # TODO(imkarrer) - Added security_groups to make tempest pass, need real groups # noqa 'security_groups': [] }}
def on_put(self, req, resp, tenant_id, server_id): client = req.env['sl_client'] cci = CCIManager(client) body = json.loads(req.stream.read().decode()) if 'name' in lookup(body, 'server'): if lookup(body, 'server', 'name').strip() == '': return bad_request(resp, message='Server name is blank') cci.edit(server_id, hostname=lookup(body, 'server', 'name')) instance = cci.get_instance(server_id, mask=get_virtual_guest_mask()) results = get_server_details_dict(self.app, req, instance) resp.body = {'server': results}
def on_put(self, req, resp, tenant_id, server_id): client = req.env["sl_client"] cci = CCIManager(client) body = json.loads(req.stream.read().decode()) if "name" in lookup(body, "server"): if lookup(body, "server", "name").strip() == "": return bad_request(resp, message="Server name is blank") cci.edit(server_id, hostname=lookup(body, "server", "name")) instance = cci.get_instance(server_id, mask=get_virtual_guest_mask()) results = get_server_details_dict(self.app, req, instance) resp.body = {"server": results}
def on_delete(self, req, resp, tenant_id, server_id): client = req.env['sl_client'] cci = CCIManager(client) try: cci.cancel_instance(server_id) except SoftLayerAPIError as e: if 'active transaction' in e.faultString: return bad_request( resp, message='Can not cancel an instance when there is already' ' an active transaction', code=409) raise resp.status = 204
def on_put(self, req, resp, tenant_id, server_id): client = req.sl_client vs = SoftLayer.VSManager(client) body = json.loads(req.stream.read().decode()) if 'name' in utils.lookup(body, 'server'): if utils.lookup(body, 'server', 'name').strip() == '': return error_handling.bad_request( resp, message='Server name is blank') vs.edit(server_id, hostname=utils.lookup(body, 'server', 'name')) instance = vs.get_instance(server_id, mask=get_virtual_guest_mask()) results = get_server_details_dict(self.app, req, instance, False) resp.body = {'server': results}
def get_volume(req, resp, tenant_id, volume_id=None): LOG.debug("Retrieving information for volume with id: %s", volume_id) # volume id are represented by uuuid.uuid4() if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message='Volume ID is too long; ' 'must be less than 11 characters ' 'in length') client = req.sl_client iscsi = client['Network_Storage_Iscsi'] vol = iscsi.getObject(id=volume_id, mask=get_network_storage_mask()) LOG.debug("volume returned from softlayer: %s", vol) resp.body = {'volume': format_volume(tenant_id, vol)} resp.status = HTTP.OK LOG.debug("response for volume details: %s", resp.body)
def on_get(self, req, resp, tenant_id, instance_id, volume_id): '''Shows details for the specified volume attachment.''' try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed request body") # since detail has the same info as the input request params, we can # just return the values back in the response using the request params. # But instead we will do sanity check to ensure the volume_id belongs # to the instance. vg_client = req.sl_client['Virtual_Guest'] try: blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type', id=instance_id) vols = [ x for x in blkDevices if x['diskImage']['type']['keyName'] != 'SWAP' ] for vol in vols: json_response = None vol_disk_id = vol['diskImage']['id'] if str(vol_disk_id) == volume_id: json_response = { "volumeAttachment": { "device": "", "id": vol_disk_id, "serverId": instance_id, "volumeId": vol_disk_id } } break if json_response: resp.body = json_response else: return error_handling.volume_fault(resp, 'Invalid volume id.', code=HTTP.BAD_REQUEST) except Exception as e: return error_handling.volume_fault(resp, e.faultString)
def on_delete(self, req, resp, tenant_id, instance_id, volume_id): """Detach the requested volume from the specified instance.""" try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed request body") vdi_client = req.env['sl_client']['Virtual_Disk_Image'] # first let's check if the volume is already attached try: volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices') blkDevices = volinfo['blockDevices'] if len(blkDevices) > 0: guestId_list = [blkDevice['guestId'] for blkDevice in blkDevices] for guest_id in guestId_list: if guest_id == instance_id: try: # detach the volume here vg_client = req.env['sl_client']['Virtual_Guest'] vg_client.detachDiskImage(volume_id, id=instance_id) break except Exception as e: error_handling.volume_fault(resp, e.faultString) else: return error_handling.volume_fault( resp, 'The requested disk image is attached to another ' 'guest and cannot be detached.', code=HTTP.BAD_REQUEST) except Exception as e: return error_handling.volume_fault(resp, e.faultString, code=500) resp.status = HTTP.ACCEPTED
def on_delete(self, req, resp, tenant_id, instance_id, volume_id): """Detach the requested volume from the specified instance.""" try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed request body") vdi_client = req.sl_client['Virtual_Disk_Image'] # first let's check if the volume is already attached try: volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices') blkDevices = volinfo['blockDevices'] if len(blkDevices) > 0: guestId_list = [ blkDevice['guestId'] for blkDevice in blkDevices ] for guest_id in guestId_list: if guest_id == instance_id: try: # detach the volume here vg_client = req.sl_client['Virtual_Guest'] vg_client.detachDiskImage(volume_id, id=instance_id) break except Exception as e: error_handling.volume_fault(resp, e.faultString) else: return error_handling.volume_fault( resp, 'The requested disk image is attached to another ' 'guest and cannot be detached.', code=HTTP.BAD_REQUEST) except Exception as e: return error_handling.volume_fault(resp, e.faultString, code=500) resp.status = HTTP.ACCEPTED
def on_get(self, req, resp, subnet_id): """Shows information for a specified subnet. (subnet-show) @param req: Http Request body @param resp: Http Response body @param subnet_id: subnet id @return: Bad request if the id is not a valid integer """ client = req.env['sl_client'] tenant_id = req.env['auth']['tenant_id'] try: subnet_id = int(subnet_id) except Exception: return error_handling.bad_request(resp, message="Malformed request body") subnet = client['Network_Subnet'].getObject(id=subnet_id, mask=SUBNET_MASK) resp.body = {'subnet': format_subnetwork(subnet, tenant_id)} resp.status = 200
def on_get(self, req, resp, subnet_id): """Shows information for a specified subnet. (subnet-show) @param req: Http Request body @param resp: Http Response body @param subnet_id: subnet id @return: Bad request if the id is not a valid integer """ client = req.sl_client tenant_id = req.env['auth']['tenant_id'] try: subnet_id = int(subnet_id) except Exception: return error_handling.bad_request(resp, message="Malformed request body") subnet = client['Network_Subnet'].getObject(id=subnet_id, mask=SUBNET_MASK) resp.body = {'subnet': format_subnetwork(subnet, tenant_id)} resp.status = 200
def on_get(self, req, resp, network_id): """Shows information for a specified network. (net-show) @param req: Http Request body @param resp: Http Response body @param network_id: Network Id @return: Http status """ client = req.env['sl_client'] tenant_id = req.env['auth']['tenant_id'] try: network_id = int(network_id) except Exception: return error_handling.bad_request(resp, message="Malformed request body") vlan = client['Network_Vlan'].getObject(id=network_id, mask=NETWORK_MASK) resp.body = {'network': format_network(vlan, tenant_id)} resp.status = 200
def on_get(self, req, resp, tenant_id, instance_id, volume_id): '''Shows details for the specified volume attachment.''' try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed request body") # since detail has the same info as the input request params, we can # just return the values back in the response using the request params. # But instead we will do sanity check to ensure the volume_id belongs # to the instance. vg_client = req.env['sl_client']['Virtual_Guest'] try: blkDevices = vg_client.getBlockDevices(mask='id, diskImage.type', id=instance_id) vols = [x for x in blkDevices if x['diskImage']['type']['keyName'] != 'SWAP'] for vol in vols: json_response = None vol_disk_id = vol['diskImage']['id'] if str(vol_disk_id) == volume_id: json_response = {"volumeAttachment": {"device": "", "id": vol_disk_id, "serverId": instance_id, "volumeId": vol_disk_id}} break if json_response: resp.body = json_response else: error_handling.volume_fault(resp, 'Invalid volume id.', code=HTTP.BAD_REQUEST) except Exception as e: error_handling.volume_fault(resp, e.faultString)
def on_post(self, req, resp, tenant_id): payload = {} client = req.sl_client body = json.loads(req.stream.read().decode()) payload['hostname'] = body['server']['name'] payload['domain'] = config.CONF['default_domain'] or 'jumpgate.com' payload['image_id'] = body['server']['imageRef'] # TODO(kmcdonald) - How do we set this accurately? payload['hourly'] = True networks = utils.lookup(body, 'server', 'networks') vs = SoftLayer.VSManager(client) try: self._handle_flavor(payload, body) self._handle_sshkeys(payload, body, client) # NOTE(mriedem): This is a hack but we need to stash the user_id # in the metadata on the virtual guest since the user's account # might not let them lookup billing information later during GET. self._stash_user_id_in_metadata(req, body) self._handle_user_data(payload, body) self._handle_datacenter(payload, body) if networks: self._handle_network(payload, client, networks) new_instance = vs.create_instance(**payload) except Exception as e: return error_handling.bad_request(resp, message=str(e)) # This should be the first tag that the VS set. Adding any more tags # will replace this tag try: flavor_id = int(body['server'].get('flavorRef')) vs = client['Virtual_Guest'] vs.setTags('{"flavor_id": ' + str(flavor_id) + '}', id=new_instance['id']) except Exception: pass resp.set_header('x-compute-request-id', 'create') resp.status = 202 resp.body = { 'server': { # Casted to string to make tempest pass 'id': str(new_instance['id']), 'links': [{ 'href': self.app.get_endpoint_url('compute', req, 'v2_server', instance_id=new_instance['id']), 'rel': 'self' }], 'adminPass': '', # TODO(imkarrer) - Added security_groups to make tempest pass, need real groups # noqa 'security_groups': [] } }
def on_post(self, req, resp, tenant_id, instance_id): body = json.loads(req.stream.read().decode()) if len(body) == 0: return error_handling.bad_request(resp, message="Malformed request body") vg_client = req.env['sl_client']['Virtual_Guest'] cci = SoftLayer.CCIManager(req.env['sl_client']) try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") instance = cci.get_instance(instance_id) if 'pause' in body or 'suspend' in body: try: vg_client.pause(id=instance_id) except SoftLayer.SoftLayerAPIError as e: if 'Unable to pause instance' in e.faultString: return error_handling.duplicate(resp, e.faultString) raise resp.status = 202 return elif 'unpause' in body or 'resume' in body: vg_client.resume(id=instance_id) resp.status = 202 return elif 'reboot' in body: if body['reboot'].get('type') == 'SOFT': vg_client.rebootSoft(id=instance_id) elif body['reboot'].get('type') == 'HARD': vg_client.rebootHard(id=instance_id) else: vg_client.rebootDefault(id=instance_id) resp.status = 202 return elif 'os-stop' in body: vg_client.powerOff(id=instance_id) resp.status = 202 return elif 'os-start' in body: vg_client.powerOn(id=instance_id) resp.status = 202 return elif 'createImage' in body: image_name = body['createImage']['name'] disks = [] for disk in filter(lambda x: x['device'] == '0', instance['blockDevices']): disks.append(disk) try: vg_client.createArchiveTransaction( image_name, disks, "Auto-created by OpenStack compatibility layer", id=instance_id, ) # Workaround for not having an image guid until the image is # fully created. TODO(nbeitenmiller): Fix this cci.wait_for_transaction(instance_id, 300) _filter = { 'privateBlockDeviceTemplateGroups': { 'name': {'operation': image_name}, 'createDate': { 'operation': 'orderBy', 'options': [{'name': 'sort', 'value': ['DESC']}], } }} acct = req.env['sl_client']['Account'] matching_image = acct.getPrivateBlockDeviceTemplateGroups( mask='id, globalIdentifier', filter=_filter, limit=1) image_guid = matching_image.get('globalIdentifier') url = self.app.get_endpoint_url('image', req, 'v2_image', image_guid=image_guid) resp.status = 202 resp.set_header('location', url) except SoftLayer.SoftLayerAPIError as e: error_handling.compute_fault(resp, e.faultString) return elif 'os-getConsoleOutput' in body: resp.status = 501 return elif 'resize' in body: flavor_id = int(body['resize'].get('flavorRef')) if flavor_id not in flavors.FLAVORS: return error_handling.bad_request( resp, message="Invalid flavor id in the request body") flavor = flavors.FLAVORS[flavor_id] cci.upgrade(instance_id, cpus=flavor['cpus'], memory=flavor['ram'] / 1024) resp.status = 202 return elif 'confirmResize' in body: resp.status = 204 return return error_handling.bad_request( resp, message="There is no such action: %s" % list(body.keys()), code=400)
def on_post(self, req, resp, tenant_id): client = req.env['sl_client'] body = json.loads(req.stream.read().decode()) flavor_id = int(body['server'].get('flavorRef')) if flavor_id not in flavors.FLAVORS: return error_handling.bad_request(resp, 'Flavor could not be found') flavor = flavors.FLAVORS[flavor_id] ssh_keys = [] key_name = body['server'].get('key_name') if key_name: sshkey_mgr = SoftLayer.SshKeyManager(client) keys = sshkey_mgr.list_keys(label=key_name) if len(keys) == 0: return error_handling.bad_request(resp, 'KeyPair could not be found') ssh_keys.append(keys[0]['id']) private_network_only = False networks = utils.lookup(body, 'server', 'networks') if networks: # Make sure they're valid networks if not all([network['uuid'] in ['public', 'private'] in network for network in networks]): return error_handling.bad_request(resp, message='Invalid network') # Find out if it's private only if not any([network['uuid'] == 'public' in network for network in networks]): private_network_only = True user_data = {} if utils.lookup(body, 'server', 'metadata'): user_data['metadata'] = utils.lookup(body, 'server', 'metadata') if utils.lookup(body, 'server', 'user_data'): user_data['user_data'] = utils.lookup(body, 'server', 'user_data') if utils.lookup(body, 'server', 'personality'): user_data['personality'] = utils.lookup(body, 'server', 'personality') datacenter = (utils.lookup(body, 'server', 'availability_zone') or config.CONF['compute']['default_availability_zone']) if not datacenter: return error_handling.bad_request(resp, 'availability_zone missing') cci = SoftLayer.CCIManager(client) payload = { 'hostname': body['server']['name'], 'domain': config.CONF['default_domain'] or 'jumpgate.com', 'cpus': flavor['cpus'], 'memory': flavor['ram'], 'local_disk': False if flavor['disk-type'] == 'SAN' else True, 'hourly': True, # TODO(kmcdonald) - How do we set this accurately? 'datacenter': datacenter, 'image_id': body['server']['imageRef'], 'ssh_keys': ssh_keys, 'private': private_network_only, 'userdata': json.dumps(user_data), } try: new_instance = cci.create_instance(**payload) except ValueError as e: return error_handling.bad_request(resp, message=str(e)) resp.set_header('x-compute-request-id', 'create') resp.status = 202 resp.body = {'server': { 'id': new_instance['id'], 'links': [{ 'href': self.app.get_endpoint_url( 'compute', req, 'v2_server', instance_id=new_instance['id']), 'rel': 'self'}], 'adminPass': '', }}
def on_post(self, req, resp, tenant_id, instance_id): body = json.loads(req.stream.read().decode()) if len(body) == 0: return bad_request(resp, message="Malformed request body") vg_client = req.env["sl_client"]["Virtual_Guest"] cci = CCIManager(req.env["sl_client"]) try: instance_id = int(instance_id) except ValueError: return not_found(resp, "Invalid instance ID specified.") instance = cci.get_instance(instance_id) if "pause" in body or "suspend" in body: try: vg_client.pause(id=instance_id) except SoftLayerAPIError as e: if "Unable to pause instance" in e.faultString: return duplicate(resp, e.faultString) raise resp.status = 202 return elif "unpause" in body or "resume" in body: vg_client.resume(id=instance_id) resp.status = 202 return elif "reboot" in body: if body["reboot"].get("type") == "SOFT": vg_client.rebootSoft(id=instance_id) elif body["reboot"].get("type") == "HARD": vg_client.rebootHard(id=instance_id) else: vg_client.rebootDefault(id=instance_id) resp.status = 202 return elif "os-stop" in body: vg_client.powerOff(id=instance_id) resp.status = 202 return elif "os-start" in body: vg_client.powerOn(id=instance_id) resp.status = 202 return elif "createImage" in body: image_name = body["createImage"]["name"] disks = [] for disk in filter(lambda x: x["device"] == "0", instance["blockDevices"]): disks.append(disk) try: vg_client.createArchiveTransaction( image_name, disks, "Auto-created by OpenStack compatibility layer", id=instance_id ) # Workaround for not having an image guid until the image is # fully created. TODO: Fix this cci.wait_for_transaction(instance_id, 300) _filter = { "privateBlockDeviceTemplateGroups": { "name": {"operation": image_name}, "createDate": {"operation": "orderBy", "options": [{"name": "sort", "value": ["DESC"]}]}, } } acct = req.env["sl_client"]["Account"] matching_image = acct.getPrivateBlockDeviceTemplateGroups( mask="id, globalIdentifier", filter=_filter, limit=1 ) image_guid = matching_image.get("globalIdentifier") url = self.app.get_endpoint_url("image", req, "v2_image", image_guid=image_guid) resp.status = 202 resp.set_header("location", url) except SoftLayerAPIError as e: compute_fault(resp, e.faultString) return elif "os-getConsoleOutput" in body: resp.status = 501 return return bad_request(resp, message="There is no such action: %s" % list(body.keys()), code=400)
def on_post(self, req, resp, tenant_id): client = req.env['sl_client'] body = json.loads(req.stream.read().decode()) flavor_id = int(body['server'].get('flavorRef')) if flavor_id not in FLAVORS: return bad_request(resp, 'Flavor could not be found') flavor = FLAVORS[flavor_id] ssh_keys = [] key_name = body['server'].get('key_name') if key_name: sshkey_mgr = SshKeyManager(client) keys = sshkey_mgr.list_keys(label=key_name) if len(keys) == 0: return bad_request(resp, 'KeyPair could not be found') ssh_keys.append(keys[0]['id']) private_network_only = False networks = lookup(body, 'server', 'networks') if networks: # Make sure they're valid networks if not all([ network['uuid'] in ['public', 'private'] in network for network in networks ]): return bad_request(resp, message='Invalid network') # Find out if it's private only if not any([ network['uuid'] == 'public' in network for network in networks ]): private_network_only = True user_data = {} if lookup(body, 'server', 'metadata'): user_data['metadata'] = lookup(body, 'server', 'metadata') if lookup(body, 'server', 'user_data'): user_data['user_data'] = lookup(body, 'server', 'user_data') if lookup(body, 'server', 'personality'): user_data['personality'] = lookup(body, 'server', 'personality') datacenter = None if lookup(body, 'server', 'availability_zone'): datacenter = lookup(body, 'server', 'availability_zone') cci = CCIManager(client) payload = { 'hostname': body['server']['name'], 'domain': 'jumpgate.com', # TODO - Don't hardcode this 'cpus': flavor['cpus'], 'memory': flavor['ram'], 'hourly': True, # TODO - How do we set this accurately? 'datacenter': datacenter, 'image_id': body['server']['imageRef'], 'ssh_keys': ssh_keys, 'private': private_network_only, 'userdata': json.dumps(user_data), } try: new_instance = cci.create_instance(**payload) except ValueError as e: return bad_request(resp, message=str(e)) resp.set_header('x-compute-request-id', 'create') resp.status = 202 resp.body = { 'server': { 'id': new_instance['id'], 'links': [{ 'href': self.app.get_endpoint_url('compute', req, 'v2_server', instance_id=new_instance['id']), 'rel': 'self' }], 'adminPass': '', } }
def on_post(self, req, resp, tenant_id): """Create volume (SL Portable storage) :param req: Falcon request object :param resp: Falcon request object :param tenant_id: Softlayer tenant_id :param return: Falcon response object with openstack response body """ client = req.env['sl_client'] try: v_type_zone = None rounding = False body = json.loads(req.stream.read().decode()) if body['volume']['volume_type'] is not None: if not self.volume_types['volume_types']: resp.status = HTTP.INTERNAL_SERVER_ERROR return error_handling.volume_fault(resp, "Server has no" " types to select") foundType = False for type in self.volume_types['volume_types']: if type['name'] == body['volume']['volume_type']: foundType = True v_type_zone = ( type['extra_specs']['capabilities:volume_backend_name'] # noqa ) rounding = ( type['extra_specs']['drivers:exact_capacity'] ) if not foundType: resp.status = 400 raise Exception('Specify a volume with a valid name') # required field in the create volume payload namestr = body['volume'].get("display_name") volreq = body['volume'] # portable storage order cannot have empty name name = (config.CONF['volume']['volume_name_prefix'] + (namestr if namestr else "")) # size is required option for volume create. Throw type exception # if it is invalid size = int(volreq['size']) # availability_zone is optional, don't throw exception if # it is not available availability_zone = (body['volume'].get('availability_zone') or v_type_zone or config.CONF['volume']['default_availability_zone']) # noqa volume_type = body['volume'].get('volume_type') except Exception as e: return error_handling.bad_request(resp, str(e)) try: volinfo = self._create_volume(tenant_id, client, resp, size, name=name, zone=availability_zone, volume_type=volume_type, exact_capacity=rounding) resp.status = HTTP.ACCEPTED if volinfo: resp.body = {'volume': format_volume(tenant_id, volinfo, client)} resp.body['volume'].update({'status': 'creating'}) else: # Cannot generate a valid response without knowning # the volume id when order takes too long to complete. # This should be a rare case, but it could break openstack # since the volume create caller always expect a volume id # uppon successful return. The approach here is to fail # the volume create operation and leak one portable storage # volume. User can always cancel from SL portal. return error_handling.volume_fault(resp, "Portable storage" " order delayed") except SoftLayer.SoftLayerAPIError as e: return error_handling.error(resp, "SoftLayerAPIError", e.faultString, code=e.faultCode) except Exception as e: return error_handling.volume_fault(resp, str(e))
def on_post(self, req, resp, tenant_id, instance_id): '''Attaches a specified volume to a specified server.''' body = json.loads(req.stream.read().decode()) if (len(body) == 0 or 'volumeAttachment' not in body or 'volumeId' not in body['volumeAttachment']): return error_handling.bad_request(resp, message="Malformed " "request body") vg_client = req.env['sl_client']['Virtual_Guest'] try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") volume_id = body['volumeAttachment']['volumeId'] if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed " "request body") vdi_client = req.env['sl_client']['Virtual_Disk_Image'] volinfo = None # first let's check if the volume is already attached try: volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices') blkDevices = volinfo['blockDevices'] if (len(blkDevices) > 0): guestId_list = [ blkDevice['guestId'] for blkDevice in blkDevices ] for guest_id in guestId_list: if (guest_id == instance_id): return error_handling.volume_fault( resp, 'The requested disk image is already attached to ' 'this guest.', code=HTTP.BAD_REQUEST) else: return error_handling.volume_fault( resp, 'The requested disk image is already attached to ' 'another guest.', code=HTTP.BAD_REQUEST) except Exception as e: return error_handling.volume_fault(resp, e.faultString, code=HTTP.NOT_FOUND) try: # providing different size doesn't seem to have any impact on the # outcome hence using 10 as default size. disk_check = vg_client.checkHostDiskAvailability(10, id=instance_id) except Exception: disk_check = True try: if disk_check: sl_transaction = vg_client.attachDiskImage(volume_id, id=instance_id) resp.body = { "volumeAttachment": { "device": "", "id": sl_transaction['id'], "serverId": instance_id, "volumeId": volume_id } } resp.status = HTTP.ACCEPTED else: return error_handling.volume_fault( resp, 'Action causes migration to a new host. Migration is not ' 'allowed.', code=HTTP.BAD_REQUEST) except Exception as e: error_handling.volume_fault(resp, e.faultString)
def on_post(self, req, resp, tenant_id, instance_id): body = json.loads(req.stream.read().decode()) if len(body) == 0: return bad_request(resp, message="Malformed request body") vg_client = req.env['sl_client']['Virtual_Guest'] cci = CCIManager(req.env['sl_client']) try: instance_id = int(instance_id) except ValueError: return not_found(resp, "Invalid instance ID specified.") instance = cci.get_instance(instance_id) if 'pause' in body or 'suspend' in body: try: vg_client.pause(id=instance_id) except SoftLayerAPIError as e: if 'Unable to pause instance' in e.faultString: return duplicate(resp, e.faultString) raise resp.status = 202 return elif 'unpause' in body or 'resume' in body: vg_client.resume(id=instance_id) resp.status = 202 return elif 'reboot' in body: if body['reboot'].get('type') == 'SOFT': vg_client.rebootSoft(id=instance_id) elif body['reboot'].get('type') == 'HARD': vg_client.rebootHard(id=instance_id) else: vg_client.rebootDefault(id=instance_id) resp.status = 202 return elif 'os-stop' in body: vg_client.powerOff(id=instance_id) resp.status = 202 return elif 'os-start' in body: vg_client.powerOn(id=instance_id) resp.status = 202 return elif 'createImage' in body: image_name = body['createImage']['name'] disks = [] for disk in filter(lambda x: x['device'] == '0', instance['blockDevices']): disks.append(disk) try: vg_client.createArchiveTransaction( image_name, disks, "Auto-created by OpenStack compatibility layer", id=instance_id, ) # Workaround for not having an image guid until the image is # fully created. TODO: Fix this cci.wait_for_transaction(instance_id, 300) _filter = { 'privateBlockDeviceTemplateGroups': { 'name': { 'operation': image_name }, 'createDate': { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC'] }], } } } acct = req.env['sl_client']['Account'] matching_image = acct.getPrivateBlockDeviceTemplateGroups( mask='id, globalIdentifier', filter=_filter, limit=1) image_guid = matching_image.get('globalIdentifier') url = self.app.get_endpoint_url('image', req, 'v2_image', image_guid=image_guid) resp.status = 202 resp.set_header('location', url) except SoftLayerAPIError as e: compute_fault(resp, e.faultString) return elif 'os-getConsoleOutput' in body: resp.status = 501 return return bad_request(resp, message="There is no such action: %s" % list(body.keys()), code=400)
def on_post(self, req, resp, tenant_id, instance_id): '''Attaches a specified volume to a specified server.''' body = json.loads(req.stream.read().decode()) if any([len(body) == 0, 'volumeAttachment' not in body, 'volumeId' not in body['volumeAttachment']]): return error_handling.bad_request(resp, message="Malformed request body") vg_client = req.env['sl_client']['Virtual_Guest'] try: instance_id = int(instance_id) except Exception: return error_handling.not_found(resp, "Invalid instance ID specified.") volume_id = body['volumeAttachment']['volumeId'] if volume_id and len(volume_id) > OPENSTACK_VOLUME_UUID_LEN: return error_handling.bad_request(resp, message="Malformed request body") vdi_client = req.env['sl_client']['Virtual_Disk_Image'] volinfo = None # first let's check if the volume is already attached try: volinfo = vdi_client.getObject(id=volume_id, mask='blockDevices') blkDevices = volinfo['blockDevices'] if (len(blkDevices) > 0): guestId_list = [blkDevice['guestId'] for blkDevice in blkDevices] for guest_id in guestId_list: if (guest_id == instance_id): return error_handling.volume_fault( resp, 'The requested disk image is already attached to ' 'this guest.', code=HTTP.BAD_REQUEST) else: return error_handling.volume_fault( resp, 'The requested disk image is already attached to ' 'another guest.', code=HTTP.BAD_REQUEST) except Exception as e: return error_handling.volume_fault(resp, e.faultString, code=HTTP.NOT_FOUND) try: # providing different size doesn't seem to have any impact on the # outcome hence using 10 as default size. disk_check = vg_client.checkHostDiskAvailability(10, id=instance_id) except Exception: disk_check = True try: if disk_check: sl_transaction = vg_client.attachDiskImage(volume_id, id=instance_id) resp.body = {"volumeAttachment": {"device": "", "id": sl_transaction['id'], "serverId": instance_id, "volumeId": volume_id}} resp.status = HTTP.ACCEPTED else: return error_handling.volume_fault( resp, 'Action causes migration to a new host. Migration is not ' 'allowed.', code=HTTP.BAD_REQUEST) except Exception as e: error_handling.volume_fault(resp, e.faultString)
def on_post(self, req, resp, tenant_id): client = req.env["sl_client"] body = json.loads(req.stream.read().decode()) flavor_id = int(body["server"].get("flavorRef")) if flavor_id not in FLAVORS: return bad_request(resp, "Flavor could not be found") flavor = FLAVORS[flavor_id] ssh_keys = [] key_name = body["server"].get("key_name") if key_name: sshkey_mgr = SshKeyManager(client) keys = sshkey_mgr.list_keys(label=key_name) if len(keys) == 0: return bad_request(resp, "KeyPair could not be found") ssh_keys.append(keys[0]["id"]) private_network_only = False networks = lookup(body, "server", "networks") if networks: # Make sure they're valid networks if not all([network["uuid"] in ["public", "private"] in network for network in networks]): return bad_request(resp, message="Invalid network") # Find out if it's private only if not any([network["uuid"] == "public" in network for network in networks]): private_network_only = True user_data = {} if lookup(body, "server", "metadata"): user_data["metadata"] = lookup(body, "server", "metadata") if lookup(body, "server", "user_data"): user_data["user_data"] = lookup(body, "server", "user_data") if lookup(body, "server", "personality"): user_data["personality"] = lookup(body, "server", "personality") datacenter = None if lookup(body, "server", "availability_zone"): datacenter = lookup(body, "server", "availability_zone") cci = CCIManager(client) payload = { "hostname": body["server"]["name"], "domain": CONF["default_domain"] or "jumpgate.com", "cpus": flavor["cpus"], "memory": flavor["ram"], "local_disk": False if flavor["disk-type"] == "SAN" else True, "hourly": True, # TODO - How do we set this accurately? "datacenter": datacenter, "image_id": body["server"]["imageRef"], "ssh_keys": ssh_keys, "private": private_network_only, "userdata": json.dumps(user_data), } try: new_instance = cci.create_instance(**payload) except ValueError as e: return bad_request(resp, message=str(e)) resp.set_header("x-compute-request-id", "create") resp.status = 202 resp.body = { "server": { "id": new_instance["id"], "links": [ { "href": self.app.get_endpoint_url("compute", req, "v2_server", instance_id=new_instance["id"]), "rel": "self", } ], "adminPass": "", } }