cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
main.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # @COPYRIGHT_begin
3 #
4 # Copyright [2010-2014] Institute of Nuclear Physics PAN, Krakow, Poland
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # @COPYRIGHT_end
19 
20 ##
21 # @package src.ec2
22 # WSGI application for EC2 API service
23 #
24 # @copyright Copyright (c) 2012 Institute of Nuclear Physics PAS <http://www.ifj.edu.pl/>
25 # @author Oleksandr Gituliar <gituliar@gmail.com>
26 # @author Łukasz Chrząszcz <l.chrzaszcz@gmail.com>
27 #
28 
29 import os
30 import sys
31 import traceback
32 import urlparse
33 
34 from ec2.base.s3action import S3Action
35 
36 
37 sys.path.append(
38  os.path.join(os.path.dirname(os.path.abspath(__file__)), '../..')
39 )
40 from ec2.settings import CLM_ADDRESS
41 from common.utils import ServerProxy
42 from ec2.base.action import Action, CLMException
43 from ec2.error import AuthFailure, EC2Exception, InvalidZone
44 import ec2.address, ec2.image, ec2.instance, ec2.key_pair, ec2.region, ec2.universal, ec2.volume, ec2.security_group
45 import ec2.s3.s3bucket, ec2.s3.s3object
46 
47 DEBUG = True
48 
49 # Validate response against Amazon EC2 XML Schema?
50 if False:
51  import lxml.etree
52  from StringIO import StringIO
53  xmlschema = lxml.etree.XMLSchema(file='./restapi/ec2/base/ec2.xsd')
54 
55  def validate(response):
56  doc = lxml.etree.parse(StringIO(response))
57  try:
58  xmlschema.assertValid(doc)
59  print "Validation [PASS]"
60  except lxml.etree.DocumentInvalid, error:
61  print "Validation [FAIL]", error
62  return response
63 else:
64  def validate(response):
65  return response
66 # musisz ustawiac cm_id w request
67 
68 
69 class CloudManager(object):
70 
71  def __init__(self, uri, aws_key=None, parameters=None, signature=None):
72  self.aws_key = aws_key
73  self.parameters = parameters
74  self.signature = signature
75 
76  self._cluster_managers_data = None
77  self._proxy_server = ServerProxy(uri)
78 
79  # @memoize
80  def cluster_managers(self):
81  if not self._cluster_managers_data:
82  self._cluster_managers_data = \
83  self._proxy_server.send_request("guest/cluster/list_names/")['data']
84 
85  print self._cluster_managers_data
86  cluster_managers = []
87  for cluster_manager_data in self._cluster_managers_data:
88  cluster_manager = ClusterManager(
89  cluster_manager_data['cluster_id'],
90  cluster_manager_data['name'],
91  self
92  )
93  cluster_managers.append(cluster_manager)
94  return cluster_managers
95 
96  def get_cluster_manager(self, by_environ=None, by_id=None, by_name=None):
97  cluster_manager = None
98  if by_environ:
99  name = by_environ['HTTP_HOST'].split('.')[0]
100  cluster_manager = self.get_cluster_manager(by_name=name)
101  else:
102  if by_id:
103  for cm in self.cluster_managers():
104  if cm.id == by_id:
105  cluster_manager = cm
106  break
107  elif by_name:
108  for cm in self.cluster_managers():
109  if cm.name == by_name:
110  cluster_manager = cm
111  break
112  return cluster_manager
113 
114 
115 class ClusterManager(object):
116 
117  def __init__(self, id_, name, cloud_manager):
118  self.cloud_manager = cloud_manager
119  self.id = id_
120  self.name = name
121 
122  self._rest_url = []
123 
124  def __call__(self, data=None):
125 
126  if not data:
127  data = {}
128 
129  data['parameters'] = self.cloud_manager.parameters
130 
131  data['login'] = self.cloud_manager.aws_key
132  data['Signature'] = self.cloud_manager.signature
133  url = '/' + '/'.join(self._rest_url) + '/'
134 
135  self._rest_url = []
136 
137  clm = self.cloud_manager._proxy_server
138 
139  if data is not None:
140  response = clm.send_request(url, **data)
141  else:
142  response = clm.send_request(url)
143  status = response['status']
144  response_data = response['data']
145 
146  if status != 'ok':
147  raise CLMException(status, url, response_data)
148  return response_data
149 
150  def __getattr__(self, name):
151  self._rest_url.append(name)
152  return self
153 
154  def __repr__(self):
155  return "<ClusterManager(id=%s, name=%s)>" % (self.id, self.name)
156 
157 
158 ##
159 # Extract EC2 parameters from GET/POST request.
160 #
161 # Args:
162 # environ <> WSGI's environment variable
163 #
164 # Returns:
165 # <dict> EC2 action's parameters
166 #
167 def _environ_to_parameters(environ):
168  method = environ['REQUEST_METHOD']
169  if method == 'POST':
170  query_string = environ['wsgi.input'].read(int(environ.get('CONTENT_LENGTH', 0)))
171  elif method == 'GET' or method == 'PUT' or method == 'HEAD':
172  query_string = environ['QUERY_STRING']
173  else:
174  raise Exception('Unsupported request method: %s' % method)
175 
176  parameters = {}
177  # we keep blank values to capture commands for S3
178  for key, value in urlparse.parse_qs(query_string, keep_blank_values=True).iteritems():
179  parameters[key] = value if len(value) != 1 else value[0]
180 # parameters[key] = value
181 
182  # if this is EC2 request
183  if parameters.get('Action'):
184  # =============== CODE FOR EC2 ================
185  if environ.get('HTTP_HOST'):
186  parameters['Endpoint'] = environ['HTTP_HOST']
187  parameters['Method'] = method
188  # =============================================
189  else:
190  # =============== CODE FOR S3 =================
191  # getting headers
192  prefix = 'HTTP_'
193  for env in environ:
194  if env.startswith(prefix):
195  parameters[env[len(prefix):].lower()] = environ[env]
196 
197  parameters['path_info'] = environ.get('PATH_INFO')
198  parameters['request_method'] = environ.get('REQUEST_METHOD')
199  parameters['query_string'] = environ.get('QUERY_STRING') or ''
200 
201  parameters['content_type'] = environ.get('CONTENT_TYPE') or ''
202  if not parameters.get('content_md5'):
203  parameters['content_md5'] = ''
204  # Those parameters are treated differently
205  # They are not sent to CM for authorization purposes
206  parameters['input'] = environ.get('wsgi.input')
207  parameters['file_wrapper'] = environ.get('wsgi.file_wrapper')
208  parameters['content_length'] = environ.get('CONTENT_LENGTH') or ''
209  # =============================================
210 
211  print 15*'=', 'PARAMETERS', 15*'='
212  for u, v in parameters.iteritems():
213  print u, ":", v
214  print 42*'='
215 
216  return parameters
217 
218 
219 ##
220 # WSGI application for EC2 API service.
221 def _application(environ, start_response):
222  print 25*'=', 'NEW REQUEST', 25*'='
223  parameters = _environ_to_parameters(environ)
224 
225  # if there's no Action param, it means that the request was made for S3
226  # ========================== START OF S3 =================================
227  if not parameters.get('Action'):
228  s3_action = S3Action(parameters)
229  try:
230  response = s3_action.execute()
231  if response['headers']:
232  headers = [('Content-Type', 'text/xml;charset=UTF-8')] + response['headers']
233  else:
234  headers = [('Content-Type', 'text/xml;charset=UTF-8')]
235  start_response('200 OK', headers)
236  response = response['body']
237  except EC2Exception, error:
238  response = error.to_xml()
239  print 'Error:', error.code, '.', error.message
240  start_response('400 Bad Request', [('Content-Type', 'application/xml;charset=UTF-8')])
241 
242  return response
243  # ============================ END OF S3 =================================
244 
245  cloud_manager = CloudManager(
246  CLM_ADDRESS,
247  aws_key=parameters.get('AWSAccessKeyId'),
248  parameters=parameters,
249  signature=parameters.get('Signature'),
250  )
251  cluster_manager_name = environ['HTTP_HOST'].split('.')[0]
252  cluster_manager = cloud_manager.get_cluster_manager(
253  by_name=cluster_manager_name
254  )
255  try:
256  if not cluster_manager:
257  raise InvalidZone.NotFound(zone_name=cluster_manager_name)
258  action = Action(parameters, cluster_manager)
259  response = action.execute()
260  start_response('200 OK', [('Content-Type', 'text/xml;charset=UTF-8')])
261  except EC2Exception, error:
262  http_code = '400 Bad Request'
263  if isinstance(error, AuthFailure):
264  http_code = '401 Unauthorized'
265  response = error.to_xml()
266  start_response(http_code, [('Content-Type', 'text/xml;charset=UTF-8')])
267  # TODO:
268  # * respect 'Accept-Encoding' HTTP header, i.e. gzip response, etc.
269  return validate(response)
270 
271 
272 def application(environ, start_response):
273  try:
274  return _application(environ, start_response)
275  except Exception, error:
276  response = handle_500(environ, error)
277  start_response('500 Internal Server Error', [('Content-Type', 'text/plain')])
278  return response
279 
280 
281 def handle_500(environ, error):
282  if DEBUG:
283  response = traceback.format_exc()
284  print response
285  else:
286  response = '500 Internal Server Error'
287  return response
288