Fix: Glance API was not happy with empty body on POST request
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / glance_dummy_api.py
1 from flask_restful import Resource
2 from flask import Response, request
3 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
4 import logging
5 import json
6
7
8 LOG = logging.getLogger("api.openstack.glance")
9
10
11 class GlanceDummyApi(BaseOpenstackDummy):
12 def __init__(self, in_ip, in_port, compute):
13 super(GlanceDummyApi, self).__init__(in_ip, in_port)
14 self.compute = compute
15 self.api.add_resource(Shutdown,
16 "/shutdown")
17 self.api.add_resource(GlanceListApiVersions,
18 "/versions")
19 self.api.add_resource(GlanceSchema,
20 "/v2/schemas/image",
21 "/v2/schemas/metadefs/namespace",
22 "/v2/schemas/metadefs/resource_type")
23 self.api.add_resource(GlanceListImagesApi,
24 "/v1/images",
25 "/v1/images/detail",
26 "/v2/images",
27 "/v2/images/detail",
28 resource_class_kwargs={'api': self})
29 self.api.add_resource(GlanceImageByIdApi,
30 "/v1/images/<id>",
31 "/v2/images/<id>",
32 resource_class_kwargs={'api': self})
33
34 def _start_flask(self):
35 LOG.info("Starting %s endpoint @ http://%s:%d" % ("GlanceDummyApi", self.ip, self.port))
36 if self.app is not None:
37 self.app.before_request(self.dump_playbook)
38 self.app.run(self.ip, self.port, debug=True, use_reloader=False)
39
40
41 class Shutdown(Resource):
42 def get(self):
43 LOG.debug(("%s is beeing shut down") % (__name__))
44 func = request.environ.get('werkzeug.server.shutdown')
45 if func is None:
46 raise RuntimeError('Not running with the Werkzeug Server')
47 func()
48
49
50 class GlanceListApiVersions(Resource):
51 def get(self):
52 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
53 resp = dict()
54 resp['versions'] = dict()
55 versions = [{
56 "status": "CURRENT",
57 "id": "v2",
58 "links": [
59 {
60 "href": request.url_root + '/v2',
61 "rel": "self"
62 }
63 ]
64 }]
65 resp['versions'] = versions
66 return Response(json.dumps(resp), status=200, mimetype='application/json')
67
68
69 class GlanceSchema(Resource):
70 def get(self):
71 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
72 resp = dict()
73 resp['name'] = 'someImageName'
74 resp['properties'] = dict()
75 # just an ugly hack to allow the openstack client to work
76 return Response(json.dumps(resp), status=200, mimetype='application/json')
77
78
79 class GlanceListImagesApi(Resource):
80 def __init__(self, api):
81 self.api = api
82
83 def get(self):
84 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
85 try:
86 resp = dict()
87 resp['next'] = None
88 resp['first'] = "/v2/images"
89 resp['schema'] = "/v2/schemas/images"
90 resp['images'] = list()
91 limit = 18
92 c = 0
93 for image in self.api.compute.images.values():
94 f = dict()
95 f['id'] = image.id
96 f['name'] = str(image.name).replace(":latest", "")
97 f['checksum'] = "2dad48f09e2a447a9bf852bcd93548c1"
98 f['container_format'] = "docker"
99 f['disk_format'] = "raw"
100 f['size'] = 1
101 f['created_at'] = "2016-03-15T15:09:07.000000"
102 f['deleted'] = False
103 f['deleted_at'] = None
104 f['is_public'] = True
105 f['min_disk'] = 1
106 f['min_ram'] = 128
107 f['owner'] = "3dad48f09e2a447a9bf852bcd93548c1"
108 f['properties'] = {}
109 f['protected'] = False
110 f['status'] = "active"
111 f['updated_at'] = "2016-03-15T15:09:07.000000"
112 f['virtual_size'] = 1
113 f['marker'] = None
114 resp['images'].append(f)
115 c+=1
116 if c > limit: # ugly hack to stop buggy glance client to do infinite requests
117 break
118 if "marker" in request.args: # ugly hack to fix pageination of openstack client
119 resp['images'] = None
120 return Response(json.dumps(resp), status=200, mimetype="application/json")
121
122 except Exception as ex:
123 LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
124 return ex.message, 500
125
126 def post(self):
127 """
128 This one is a real fake! It does not really create anything and the mentioned image
129 should already be registered with Docker. However, this function returns a reply that looks
130 like the image was just created to make orchestrators, like OSM, happy.
131 """
132 LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
133 try:
134 body_data = json.loads(request.data)
135 except:
136 body_data = dict()
137 # lets see what we should create
138 img_name = request.headers.get("X-Image-Meta-Name")
139 img_size = request.headers.get("X-Image-Meta-Size")
140 img_disk_format = request.headers.get("X-Image-Meta-Disk-Format")
141 img_is_public = request.headers.get("X-Image-Meta-Is-Public")
142 img_container_format = request.headers.get("X-Image-Meta-Container-Format")
143 # try to use body payload if header fields are empty
144 if img_name is None:
145 img_name = body_data.get("name")
146 img_size = 1234
147 img_disk_format = body_data.get("disk_format")
148 img_is_public = True if "public" in body_data.get("visibility") else False
149 img_container_format = body_data.get("container_format")
150 # try to find ID of already existing image (matched by name)
151 img_id=None
152 for image in self.api.compute.images.values():
153 if str(img_name) in image.name:
154 img_id = image.id
155 LOG.debug("Image name: %s" % img_name)
156 LOG.debug("Image id: %s" % img_id)
157 # build a response body that looks like a real one
158 resp = dict()
159 f = dict()
160 f['id'] = img_id
161 f['name'] = img_name
162 f['checksum'] = "2dad48f09e2a447a9bf852bcd93548c1"
163 f['container_format'] = img_container_format
164 f['disk_format'] = img_disk_format
165 f['size'] = img_size
166 f['created_at'] = "2016-03-15T15:09:07.000000"
167 f['deleted'] = False
168 f['deleted_at'] = None
169 f['is_public'] = img_is_public
170 f['min_disk'] = 1
171 f['min_ram'] = 128
172 f['owner'] = "3dad48f09e2a447a9bf852bcd93548c1"
173 f['properties'] = {}
174 f['protected'] = False
175 f['status'] = "active"
176 f['updated_at'] = "2016-03-15T15:09:07.000000"
177 f['virtual_size'] = 1
178 resp['image'] = f
179 # build actual response with headers and everything
180 r = Response(json.dumps(resp), status=201, mimetype="application/json")
181 r.headers.add("Location", "http://%s:%d/v1/images/%s" % (self.api.ip,
182 self.api.port,
183 img_id))
184 return r
185
186
187 class GlanceImageByIdApi(Resource):
188 def __init__(self, api):
189 self.api = api
190
191 def get(self, id):
192 LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
193 from emuvim.api.heat.openstack_dummies.nova_dummy_api import NovaListImages
194 nova = NovaListImages(self.api)
195 return nova.get(id)
196
197 def put(self, id):
198 LOG.debug("API CALL: %s " % str(self.__class__.__name__))
199 LOG.warning("Endpoint not implemented")
200 return None
201
202