More rackspace API.
This commit is contained in:
@@ -55,7 +55,7 @@ logging.getLogger().setLevel(logging.DEBUG)
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.warn('Starting compute node')
|
logging.warn('Starting compute node')
|
||||||
n = node.NetworkNode()
|
n = node.Node()
|
||||||
d = n.adopt_instances()
|
d = n.adopt_instances()
|
||||||
d.addCallback(lambda x: logging.info('Adopted %d instances', x))
|
d.addCallback(lambda x: logging.info('Adopted %d instances', x))
|
||||||
|
|
||||||
|
|||||||
33
exercise_rsapi.py
Normal file
33
exercise_rsapi.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import cloudservers
|
||||||
|
|
||||||
|
class IdFake:
|
||||||
|
def __init__(self, id):
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
# to get your access key:
|
||||||
|
# from nova.auth import users
|
||||||
|
# users.UserManger.instance().get_users()[0].access
|
||||||
|
rscloud = cloudservers.CloudServers(
|
||||||
|
'admin',
|
||||||
|
'6cca875e-5ab3-4c60-9852-abf5c5c60cc6'
|
||||||
|
)
|
||||||
|
rscloud.client.AUTH_URL = 'http://localhost:8773/v1.0'
|
||||||
|
|
||||||
|
|
||||||
|
rv = rscloud.servers.list()
|
||||||
|
print "SERVERS: %s" % rv
|
||||||
|
|
||||||
|
if len(rv) == 0:
|
||||||
|
server = rscloud.servers.create(
|
||||||
|
"test-server",
|
||||||
|
IdFake("ami-tiny"),
|
||||||
|
IdFake("m1.tiny")
|
||||||
|
)
|
||||||
|
print "LAUNCH: %s" % server
|
||||||
|
else:
|
||||||
|
server = rv[0]
|
||||||
|
print "Server to kill: %s" % server
|
||||||
|
|
||||||
|
raw_input("press enter key to kill the server")
|
||||||
|
|
||||||
|
server.delete()
|
||||||
@@ -40,18 +40,12 @@ from nova.endpoint import wsgi
|
|||||||
from nova.endpoint import images
|
from nova.endpoint import images
|
||||||
from nova.volume import storage
|
from nova.volume import storage
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
flags.DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
||||||
|
|
||||||
def _gen_key(user_id, key_name):
|
|
||||||
""" Tuck this into UserManager """
|
# TODO(todd): subclass Exception so we can bubble meaningful errors
|
||||||
try:
|
|
||||||
manager = users.UserManager.instance()
|
|
||||||
private_key, fingerprint = manager.generate_key_pair(user_id, key_name)
|
|
||||||
except Exception as ex:
|
|
||||||
return {'exception': ex}
|
|
||||||
return {'private_key': private_key, 'fingerprint': fingerprint}
|
|
||||||
|
|
||||||
|
|
||||||
class Api(object):
|
class Api(object):
|
||||||
@@ -59,14 +53,18 @@ class Api(object):
|
|||||||
def __init__(self, rpc_mechanism):
|
def __init__(self, rpc_mechanism):
|
||||||
self.controllers = {
|
self.controllers = {
|
||||||
"v1.0": RackspaceAuthenticationApi(),
|
"v1.0": RackspaceAuthenticationApi(),
|
||||||
"server": RackspaceCloudServerApi()
|
"servers": RackspaceCloudServerApi()
|
||||||
}
|
}
|
||||||
self.rpc_mechanism = rpc_mechanism
|
self.rpc_mechanism = rpc_mechanism
|
||||||
|
|
||||||
def handler(self, environ, responder):
|
def handler(self, environ, responder):
|
||||||
logging.error("*** %s" % environ)
|
environ['nova.context'] = self.build_context(environ)
|
||||||
controller, path = wsgi.Util.route(environ['PATH_INFO'], self.controllers)
|
controller, path = wsgi.Util.route(
|
||||||
|
environ['PATH_INFO'],
|
||||||
|
self.controllers
|
||||||
|
)
|
||||||
if not controller:
|
if not controller:
|
||||||
|
# TODO(todd): Exception (404)
|
||||||
raise Exception("Missing Controller")
|
raise Exception("Missing Controller")
|
||||||
rv = controller.process(path, environ)
|
rv = controller.process(path, environ)
|
||||||
if type(rv) is tuple:
|
if type(rv) is tuple:
|
||||||
@@ -76,8 +74,25 @@ class Api(object):
|
|||||||
responder("200 OK", [])
|
responder("200 OK", [])
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
def build_context(self, env):
|
||||||
|
rv = {}
|
||||||
|
if env.has_key("HTTP_X_AUTH_TOKEN"):
|
||||||
|
rv['user'] = users.UserManager.instance().get_user_from_access_key(
|
||||||
|
env['HTTP_X_AUTH_TOKEN']
|
||||||
|
)
|
||||||
|
if rv['user']:
|
||||||
|
rv['project'] = users.UserManager.instance().get_project(
|
||||||
|
rv['user'].name
|
||||||
|
)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
class RackspaceApiEndpoint(object):
|
class RackspaceApiEndpoint(object):
|
||||||
def process(self, path, env):
|
def process(self, path, env):
|
||||||
|
if not self.check_authentication(env):
|
||||||
|
# TODO(todd): Exception (Unauthorized)
|
||||||
|
raise Exception("Unable to authenticate")
|
||||||
|
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
return self.index(env)
|
return self.index(env)
|
||||||
|
|
||||||
@@ -86,26 +101,126 @@ class RackspaceApiEndpoint(object):
|
|||||||
method = getattr(self, action)
|
method = getattr(self, action)
|
||||||
return method(path, env)
|
return method(path, env)
|
||||||
else:
|
else:
|
||||||
|
# TODO(todd): Exception (404)
|
||||||
raise Exception("Missing method %s" % path[0])
|
raise Exception("Missing method %s" % path[0])
|
||||||
|
|
||||||
|
def check_authentication(self, env):
|
||||||
|
if hasattr(self, "process_without_authentication") \
|
||||||
|
and getattr(self, "process_without_authentication"):
|
||||||
|
return True
|
||||||
|
if not env['nova.context']['user']:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class RackspaceAuthenticationApi(RackspaceApiEndpoint):
|
class RackspaceAuthenticationApi(RackspaceApiEndpoint):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.process_without_authentication = True
|
||||||
|
|
||||||
|
# TODO(todd): make a actual session with a unique token
|
||||||
|
# just pass the auth key back through for now
|
||||||
def index(self, env):
|
def index(self, env):
|
||||||
response = '204 No Content'
|
response = '204 No Content'
|
||||||
headers = [
|
headers = [
|
||||||
('X-Server-Management-Url', 'http://localhost:8773/server'),
|
('X-Server-Management-Url', 'http://%s' % env['HTTP_HOST']),
|
||||||
('X-Storage-Url', 'http://localhost:8773/server'),
|
('X-Storage-Url', 'http://%s' % env['HTTP_HOST']),
|
||||||
('X-CDN-Managment-Url', 'http://localhost:8773/server'),
|
('X-CDN-Managment-Url', 'http://%s' % env['HTTP_HOST']),
|
||||||
|
('X-Auth-Token', env['HTTP_X_AUTH_KEY'])
|
||||||
]
|
]
|
||||||
body = ""
|
body = ""
|
||||||
return (response, headers, body)
|
return (response, headers, body)
|
||||||
|
|
||||||
|
|
||||||
class RackspaceCloudServerApi(object):
|
class RackspaceCloudServerApi(RackspaceApiEndpoint):
|
||||||
|
|
||||||
def index(self):
|
def __init__(self):
|
||||||
return "IDX"
|
self.instdir = model.InstanceDirectory()
|
||||||
|
self.network = network.PublicNetworkController()
|
||||||
|
|
||||||
def list(self, args):
|
def index(self, env):
|
||||||
return "%s" % args
|
if env['REQUEST_METHOD'] == 'GET':
|
||||||
|
return self.detail(env)
|
||||||
|
elif env['REQUEST_METHOD'] == 'POST':
|
||||||
|
return self.launch_server(env)
|
||||||
|
|
||||||
|
def detail(self, args, env):
|
||||||
|
value = {
|
||||||
|
"servers":
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
for inst in self.instdir.all:
|
||||||
|
value["servers"].append(self.instance_details(inst))
|
||||||
|
|
||||||
|
return json.dumps(value)
|
||||||
|
|
||||||
|
##
|
||||||
|
##
|
||||||
|
|
||||||
|
def launch_server(self, env):
|
||||||
|
data = json.loads(env['wsgi.input'].read(int(env['CONTENT_LENGTH'])))
|
||||||
|
inst = self.build_server_instance(data, env['nova.context'])
|
||||||
|
self.schedule_launch_of_instance(inst)
|
||||||
|
return json.dumps({"server": self.instance_details(inst)})
|
||||||
|
|
||||||
|
def instance_details(self, inst):
|
||||||
|
return {
|
||||||
|
"id": inst.get("instance_id", None),
|
||||||
|
"imageId": inst.get("image_id", None),
|
||||||
|
"flavorId": inst.get("instacne_type", None),
|
||||||
|
"hostId": inst.get("node_name", None),
|
||||||
|
"status": inst.get("state", "pending"),
|
||||||
|
"addresses": {
|
||||||
|
"public": [self.network.get_public_ip_for_instance(
|
||||||
|
inst.get("instance_id", None)
|
||||||
|
)],
|
||||||
|
"private": [inst.get("private_dns_name", None)]
|
||||||
|
},
|
||||||
|
|
||||||
|
# implemented only by Rackspace, not AWS
|
||||||
|
"name": inst.get("name", "Not-Specified"),
|
||||||
|
|
||||||
|
# not supported
|
||||||
|
"progress": "Not-Supported",
|
||||||
|
"metadata": {
|
||||||
|
"Server Label": "Not-Supported",
|
||||||
|
"Image Version": "Not-Supported"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_server_instance(self, env, context):
|
||||||
|
reservation = utils.generate_uid('r')
|
||||||
|
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||||
|
inst = self.instdir.new()
|
||||||
|
inst['name'] = env['server']['name']
|
||||||
|
inst['image_id'] = env['server']['imageId']
|
||||||
|
inst['instance_type'] = env['server']['flavorId']
|
||||||
|
inst['user_id'] = context['user'].id
|
||||||
|
inst['project_id'] = context['project'].id
|
||||||
|
inst['reservation_id'] = reservation
|
||||||
|
inst['launch_time'] = ltime
|
||||||
|
inst['mac_address'] = utils.generate_mac()
|
||||||
|
address = network.allocate_ip(
|
||||||
|
inst['user_id'],
|
||||||
|
inst['project_id'],
|
||||||
|
mac=inst['mac_address']
|
||||||
|
)
|
||||||
|
inst['private_dns_name'] = str(address)
|
||||||
|
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
|
||||||
|
inst['user_id'],
|
||||||
|
inst['project_id'],
|
||||||
|
'default' # security group
|
||||||
|
)['bridge_name']
|
||||||
|
# key_data, key_name, ami_launch_index
|
||||||
|
# TODO(todd): key data or root password
|
||||||
|
inst.save()
|
||||||
|
return inst
|
||||||
|
|
||||||
|
def schedule_launch_of_instance(self, inst):
|
||||||
|
rpc.cast(
|
||||||
|
FLAGS.compute_topic,
|
||||||
|
{
|
||||||
|
"method": "run_instance",
|
||||||
|
"args": {"instance_id": inst.instance_id}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user