[pkg-eucalyptus-commits] [SCM] managing cloud instances for Eucalyptus branch, master, updated. 3.0.0-alpha3-257-g1da8e3a
Garrett Holmstrom
gholms at fedoraproject.org
Sun Jun 16 02:31:12 UTC 2013
The following commit has been merged in the master branch:
commit 25f140a0934437aed0870d5dcbfbf8883a7d910e
Author: Garrett Holmstrom <gholms at fedoraproject.org>
Date: Tue Apr 30 11:36:01 2013 -0700
Port EucaRsaV2Auth to requestbuilder
This commit also updates euca-check-bucket to make it use the NC's auth
mechanism again.
Fixes TOOLS-269
diff --git a/bin/euca-check-bucket b/bin/euca-check-bucket
index 403a293..28f3578 100755
--- a/bin/euca-check-bucket
+++ b/bin/euca-check-bucket
@@ -1,6 +1,6 @@
#!/usr/bin/python -tt
-import euca2ools.commands.walrus.checkbucket
+import euca2ools.nc.commands.checkbucket
if __name__ == '__main__':
- euca2ools.commands.walrus.checkbucket.CheckBucket.run()
+ euca2ools.nc.commands.checkbucket.CheckBucket.run()
diff --git a/euca2ools/commands/walrus/checkbucket.py b/euca2ools/commands/walrus/checkbucket.py
index 71518a8..cfb8306 100644
--- a/euca2ools/commands/walrus/checkbucket.py
+++ b/euca2ools/commands/walrus/checkbucket.py
@@ -29,9 +29,7 @@
# POSSIBILITY OF SUCH DAMAGE.
from euca2ools.commands.walrus import WalrusRequest
-from euca2ools.exceptions import AWSError
from requestbuilder import Arg
-from requestbuilder.exceptions import ServerError
class CheckBucket(WalrusRequest):
diff --git a/euca2ools/nc/__init__.py b/euca2ools/nc/__init__.py
index 839d607..751289e 100644
--- a/euca2ools/nc/__init__.py
+++ b/euca2ools/nc/__init__.py
@@ -1,6 +1,6 @@
# Software License Agreement (BSD License)
#
-# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
+# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
# All rights reserved.
#
# Redistribution and use of this software in source and binary forms, with or
@@ -27,6 +27,3 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
-# Author: Mitch Garnaat mgarnaat at eucalyptus.com
-
diff --git a/euca2ools/nc/auth.py b/euca2ools/nc/auth.py
index 4a4f398..b17342b 100644
--- a/euca2ools/nc/auth.py
+++ b/euca2ools/nc/auth.py
@@ -1,6 +1,6 @@
# Software License Agreement (BSD License)
#
-# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
+# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
# All rights reserved.
#
# Redistribution and use of this software in source and binary forms, with or
@@ -27,146 +27,128 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
-# Author: Mitch Garnaat mgarnaat at eucalyptus.com
-import M2Crypto
+import argparse
import base64
-import boto.auth_handler
import datetime
import hashlib
-import hmac
-import time
+import os.path
+from requestbuilder import Arg
+from requestbuilder.auth import BaseAuth
+from requestbuilder.exceptions import ArgumentError
+import subprocess
+import urlparse
import urllib
-import warnings
-from boto.exception import BotoClientError
-
-class EucaRsaAuthV1Handler(boto.auth_handler.AuthHandler):
- """Provides Eucalyptus NC Authentication."""
-
- capability = ['euca-rsa-v1', 'euca-nc']
-
- def __init__(self, host, config, provider):
- boto.auth_handler.AuthHandler.__init__(self, host, config, provider)
- self.hmac = hmac.new(provider.secret_key, digestmod=hashlib.sha1)
- self.private_key_path = None
-
- def _calc_signature(self, params, headers, verb, path):
- boto.log.debug('using euca_signature')
- string_to_sign = '%s\n%s\n%s\n' % (verb, headers['Date'], path)
- keys = params.keys()
- keys.sort()
- pairs = []
- for key in keys:
- val = params[key]
- pairs.append(urllib.quote(key, safe='') + '=' + urllib.quote(val, safe='-_~'))
- qs = '&'.join(pairs)
- boto.log.debug('query string: %s' % qs)
- string_to_sign += qs
- hmac = self.hmac.copy()
- hmac.update(string_to_sign)
- sha_manifest = hashlib.sha1()
- sha_manifest.update(string_to_sign)
- private_key = M2Crypto.RSA.load_key(self.private_key_path)
- signature_value = private_key.sign(sha_manifest.digest())
- b64 = base64.b64encode(signature_value)
- boto.log.debug('len(b64)=%d' % len(b64))
- boto.log.debug('base64 encoded digest: %s' % b64)
- return (qs, b64)
-
- def add_auth(self, http_request, **kwargs):
- headers = http_request.headers
- params = http_request.params
- qs, signature = self._calc_signature(http_request.params,
- http_request.headers,
- http_request.method,
- http_request.path)
- headers['EucaSignature'] = signature
- boto.log.debug('query_string: %s Signature: %s' % (qs, signature))
- if http_request.method == 'POST':
- headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
- http_request.body = qs
- else:
- http_request.body = ''
-
-
-class EucaNCAuthHandler(EucaRsaAuthV1Handler):
- # For API compatibility
-
- def __init__(self, host, config, provider):
- warnings.warn(('EucaNCAuthHandler has been renamed to '
- 'EucaRsaAuthV1Handler'), DeprecationWarning)
- EucaRsaAuthV1Handler.__init__(self, host, config, provider)
-
-
-class EucaRsaAuthV2Handler(boto.auth_handler.AuthHandler):
- '''Provides authentication for inter-component requests'''
- capability = ['euca-rsa-v2']
- def __init__(self, host, config, provider):
- boto.auth_handler.AuthHandler.__init__(self, host, config, provider)
- self.cert_path = None
- self.private_key_path = None
+class EucaRsaV2Auth(BaseAuth):
+ '''Provides authentication for inter-component requests'''
- def add_auth(self, http_request, **kwargs):
- if 'Authorization' in http_request.headers:
- del http_request.headers['Authorization']
+ ARGS = [Arg('--cert', metavar='FILE', help='''file containing the X.509
+ certificate to use when signing requests'''),
+ Arg('--privatekey', metavar='FILE',
+ help='file containing the private key to sign requests with'),
+ Arg('--euca-auth', action='store_true', help=argparse.SUPPRESS)]
+
+ def configure(self):
+ BaseAuth.configure(self)
+ cert = self.args.get('cert') or os.getenv('EUCA_CERT')
+ privkey = self.args.get('privatekey') or os.getenv('EUCA_PRIVATE_KEY')
+ if not cert:
+ raise ArgumentError('argument --cert or environment variable '
+ 'EUCA_CERT is required')
+ if not privkey:
+ raise ArgumentError('argument --privatekey or environment '
+ 'variable EUCA_PRIVATE_KEY is required')
+ cert = os.path.expanduser(os.path.expandvars(cert))
+ privkey = os.path.expanduser(os.path.expandvars(privkey))
+ if not os.path.exists(cert):
+ raise ArgumentError("certificate file '{0}' does not exist"
+ .format(cert))
+ if not os.path.isfile(cert):
+ raise ArgumentError("certificate file '{0}' is not a file"
+ .format(cert))
+ if not os.path.exists(privkey):
+ raise ArgumentError("private key file '{0}' does not exist"
+ .format(privkey))
+ if not os.path.isfile(privkey):
+ raise ArgumentError("private key file '{0}' is not a file"
+ .format(privkey))
+ self.args['cert'] = cert
+ self.args['privatekey'] = privkey
+
+ def __call__(self, request):
+ if request.headers is None:
+ request.headers = {}
now = datetime.datetime.utcnow()
- http_request.headers['Date'] = now.strftime('%Y%m%dT%H%M%SZ')
+ request.headers['Date'] = now.strftime('%Y%m%dT%H%M%SZ')
+ if 'Authorization' in request.headers:
+ del request.headers['Authorization']
cert_fp = self._get_fingerprint()
+ self.log.debug('certificate fingerprint: %s', cert_fp)
- headers_to_sign = self._get_headers_to_sign(http_request)
+ headers_to_sign = self._get_headers_to_sign(request)
signed_headers = self._get_signed_headers(headers_to_sign)
- boto.log.debug('SignedHeaders:%s', signed_headers)
+ self.log.debug('SignedHeaders:%s', signed_headers)
- canonical_request = self._get_canonical_request(http_request)
- boto.log.debug('CanonicalRequest:\n%s', canonical_request)
+ canonical_request = self._get_canonical_request(request)
+ self.log.debug('CanonicalRequest:\n%s', canonical_request)
signature = self._sign(canonical_request)
- boto.log.debug('Signature:%s', signature)
+ self.log.debug('Signature:%s', signature)
auth_header = ' '.join(('EUCA2-RSA-SHA256', cert_fp, signed_headers,
signature))
- http_request.headers['Authorization'] = auth_header
+ request.headers['Authorization'] = auth_header
def _get_fingerprint(self):
- cert = M2Crypto.X509.load_cert(self.cert_path)
- return cert.get_fingerprint().lower()
+ cmd = ['openssl', 'x509', '-noout', '-in', self.args['cert'],
+ '-fingerprint', '-md5']
+ openssl = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ (stdout, __) = openssl.communicate()
+ if openssl.returncode != 0:
+ raise subprocess.CalledProcessError(openssl.returncode, cmd)
+ return stdout.strip().rsplit('=', 1)[-1].replace(':', '').lower()
def _sign(self, canonical_request):
- privkey = M2Crypto.RSA.load_key(self.private_key_path)
- digest = hashlib.sha256(canonical_request).digest()
- return base64.b64encode(privkey.sign(digest, algo='sha256'))
-
- def _get_canonical_request(self, http_request):
+ digest = hashlib.sha256(canonical_request).digest()
+ cmd = ['openssl', 'pkeyutl', '-sign', '-inkey',
+ self.args['privatekey'], '-pkeyopt', 'digest:sha256']
+ openssl = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ (stdout, __) = openssl.communicate(digest)
+ if openssl.returncode != 0:
+ raise subprocess.CalledProcessError(openssl.returncode, cmd)
+ return base64.b64encode(stdout)
+
+ def _get_canonical_request(self, request):
# 1. request method
- method = http_request.method.upper()
+ method = request.method.upper()
# 2. CanonicalURI
- c_uri = self._get_canonical_uri(http_request)
+ c_uri = self._get_canonical_uri(request)
# 3. CanonicalQueryString
- c_querystr = self._get_canonical_querystr(http_request)
+ c_querystr = self._get_canonical_querystr(request)
# 4. CanonicalHeaders
- headers_to_sign = self._get_headers_to_sign(http_request)
+ headers_to_sign = self._get_headers_to_sign(request)
c_headers = self._get_canonical_headers(headers_to_sign)
# 5. SignedHeaders
s_headers = self._get_signed_headers(headers_to_sign)
return '\n'.join((method, c_uri, c_querystr, c_headers, s_headers))
- def _get_canonical_uri(self, http_request):
- return http_request.path or '/'
+ def _get_canonical_uri(self, request):
+ return urlparse.urlparse(request.url).path or '/'
- def _get_canonical_querystr(self, http_request):
+ def _get_canonical_querystr(self, request):
params = []
- for key, val in http_request.params.iteritems():
- params.append(urllib.quote(param, safe='/~') + '=' +
- urllib.quote(str(val), safe='~'))
+ for key, val in request.params.iteritems():
+ params.append('='.join((urllib.quote(key, safe='/~'),
+ urllib.quote(str(val), safe='~'))))
return '&'.join(sorted(params))
- def _get_headers_to_sign(self, http_request):
- headers = {'Host': http_request.host}
- for key, val in http_request.headers.iteritems():
+ def _get_headers_to_sign(self, request):
+ headers = {'Host': urlparse.urlparse(request.url).netloc}
+ for key, val in request.headers.iteritems():
if key.lower() != 'authorization':
headers[key] = val
return headers
diff --git a/euca2ools/nc/__init__.py b/euca2ools/nc/commands/__init__.py
similarity index 93%
copy from euca2ools/nc/__init__.py
copy to euca2ools/nc/commands/__init__.py
index 839d607..505e8cd 100644
--- a/euca2ools/nc/__init__.py
+++ b/euca2ools/nc/commands/__init__.py
@@ -1,6 +1,6 @@
# Software License Agreement (BSD License)
#
-# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
+# Copyright (c) 2013, Eucalyptus Systems, Inc.
# All rights reserved.
#
# Redistribution and use of this software in source and binary forms, with or
@@ -27,6 +27,3 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
-# Author: Mitch Garnaat mgarnaat at eucalyptus.com
-
diff --git a/euca2ools/commands/monitoring/deletealarms.py b/euca2ools/nc/commands/checkbucket.py
similarity index 76%
copy from euca2ools/commands/monitoring/deletealarms.py
copy to euca2ools/nc/commands/checkbucket.py
index a86d2ef..864d388 100644
--- a/euca2ools/commands/monitoring/deletealarms.py
+++ b/euca2ools/nc/commands/checkbucket.py
@@ -29,13 +29,14 @@
# POSSIBILITY OF SUCH DAMAGE.
import argparse
-from euca2ools.commands.monitoring import CloudWatchRequest
+import euca2ools.commands.walrus.checkbucket
+import euca2ools.nc.services
from requestbuilder import Arg
-class DeleteAlarms(CloudWatchRequest):
- DESCRIPTION = 'Delete alarms'
- ARGS = [Arg('AlarmNames.member', metavar='ALARM', nargs='+',
- help='names of the alarms to delete'),
- Arg('-f', '--force', action='store_true', route_to=None,
- help=argparse.SUPPRESS)] # for compatibility
+class CheckBucket(euca2ools.commands.walrus.checkbucket.CheckBucket):
+ DESCRIPTION = ('[Eucalyptus NC internal] Return successfully if a bucket '
+ 'already exists')
+ SERVICE_CLASS = euca2ools.nc.services.NCInternalWalrus
+ ARGS = [Arg('-b', action='store_true', route_to=None,
+ help=argparse.SUPPRESS)] # This makes the -b optional
diff --git a/euca2ools/nc/connection.py b/euca2ools/nc/connection.py
deleted file mode 100644
index 0300fff..0000000
--- a/euca2ools/nc/connection.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# Software License Agreement (BSD License)
-#
-# Copyright (c) 2009-2011, Eucalyptus Systems, Inc.
-# All rights reserved.
-#
-# Redistribution and use of this software in source and binary forms, with or
-# without modification, are permitted provided that the following conditions
-# are met:
-#
-# Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the
-# following disclaimer.
-#
-# Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the
-# following disclaimer in the documentation and/or other
-# materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-# Author: Mitch Garnaat mgarnaat at eucalyptus.com
-
-import base64
-import binascii
-import boto
-import hashlib
-import time
-import urllib
-from boto.connection import AWSAuthConnection
-from boto.exception import BotoClientError, S3ResponseError, S3CreateError
-from boto.resultset import ResultSet
-from boto.s3.bucket import Bucket
-
-class EucaConnection(AWSAuthConnection):
-
- DefaultHost = 'localhost'
- DefaultPort = 8773
-
- def __init__(self, private_key_path, cert_path,
- aws_access_key_id=None, aws_secret_access_key=None,
- is_secure=False, port=DefaultPort, proxy=None, proxy_port=None,
- proxy_user=None, proxy_pass=None,
- host=DefaultHost, debug=0, https_connection_factory=None,
- path='/'):
- self.private_key_path = private_key_path
- self.cert_path = cert_path
- AWSAuthConnection.__init__(self, host, aws_access_key_id,
- aws_secret_access_key,
- is_secure, port, proxy, proxy_port,
- proxy_user, proxy_pass,
- debug=debug,
- https_connection_factory=https_connection_factory,
- path=path)
- self._auth_handler.cert_path = self.cert_path
- self._auth_handler.private_key_path = self.private_key_path
-
- def _required_auth_capability(self):
- return ['euca-rsa-v2']
-
- def make_request(self, method='GET', bucket='', key='', headers=None,
- data='', query_args=None, sender=None,
- override_num_retries=None, action=None,
- effective_user_id = None, params=None):
- if headers is None:
- headers = {}
- if params is None:
- params = {}
- if not effective_user_id:
- effective_user_id = self.aws_access_key_id
- if action:
- headers['EucaOperation'] = action
- headers['AWSAccessKeyId'] = effective_user_id
- cert_file = open(self.cert_path, 'r')
- cert_str = cert_file.read()
- cert_file.close()
- if not 'Content-Length' in headers:
- headers['Content-Length'] = str(len(data))
- if not 'Content-MD5' in headers:
- md5sum = hashlib.md5(data).digest()
- headers['Content-MD5'] = binascii.hexlify(md5sum)
- utf8_params = {}
- for key in params:
- utf8_params[key] = self.get_utf8_value(params[key])
- path_base = '/'
- path_base += "%s/" % bucket
- path = path_base + urllib.quote(key)
- http_request = self.build_base_http_request(method, path, None,
- utf8_params,
- headers, data,
- self.server_name())
- # Use EUCA2 signing
- response = self._mexe(http_request, sender)
- if response.status == 403:
- # Use EUCA signing in case we're talking to older Eucalyptus
- headers['EucaEffectiveUserId'] = effective_user_id
- headers['EucaCert'] = base64.b64encode(cert_str)
- headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
- time.gmtime())
- http_request = self.build_base_http_request(method, path, None,
- utf8_params,
- headers, data,
- self.server_name())
- self._auth_handler = boto.auth.get_auth_handler(self.host,
- boto.config,
- self.provider,
- ['euca-rsa-v1'])
- self._auth_handler.private_key_path = self.private_key_path
- response = self._mexe(http_request, sender)
- return response
-
- def get_bucket(self, bucket_name, validate=True, headers=None):
- bucket = Bucket(self, bucket_name)
- if validate:
- bucket.get_all_keys(headers, maxkeys=0)
- return bucket
-
- def create_bucket(self, bucket_name, headers=None,
- location='', policy=None):
- """
- Creates a new located bucket. By default it's in the USA. You can pass
- Location.EU to create an European bucket.
-
- :type bucket_name: string
- :param bucket_name: The name of the new bucket
-
- :type headers: dict
- :param headers: Additional headers to pass along with
- the request to AWS.
-
- :type location: :class:`boto.s3.connection.Location`
- :param location: The location of the new bucket
-
- :type policy: :class:`boto.s3.acl.CannedACLStrings`
- :param policy: A canned ACL policy that will be applied
- to the new key in S3.
-
- """
- if not bucket_name.islower():
- raise BotoClientError("Bucket names must be lower case.")
-
- if policy:
- if headers:
- headers['x-amz-acl'] = policy
- else:
- headers = {'x-amz-acl' : policy}
- if location == '':
- data = ''
- else:
- data = '<CreateBucketConstraint><LocationConstraint>' + \
- location + '</LocationConstraint></CreateBucketConstraint>'
- response = self.make_request('PUT', bucket_name, headers=headers,
- data=data)
- body = response.read()
- if response.status == 409:
- raise S3CreateError(response.status, response.reason, body)
- if response.status == 200:
- return Bucket(self, bucket_name)
- else:
- raise self.provider.storage_response_error(
- response.status, response.reason, body)
-
- def delete_bucket(self, bucket, headers=None):
- response = self.make_request('DELETE', bucket, headers=headers)
- body = response.read()
- if response.status != 204:
- raise self.provider.storage_response_error(
- response.status, response.reason, body)
-
-
diff --git a/bin/euca-version b/euca2ools/nc/services.py
old mode 100755
new mode 100644
similarity index 86%
copy from bin/euca-version
copy to euca2ools/nc/services.py
index 33bbc34..672da94
--- a/bin/euca-version
+++ b/euca2ools/nc/services.py
@@ -1,8 +1,6 @@
-#!/usr/bin/python -tt
-
# Software License Agreement (BSD License)
#
-# Copyright (c) 2009-2013, Eucalyptus Systems, Inc.
+# Copyright (c) 2013, Eucalyptus Systems, Inc.
# All rights reserved.
#
# Redistribution and use of this software in source and binary forms, with or
@@ -30,7 +28,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-import euca2ools.commands
-import sys
+import euca2ools.nc.auth
+import euca2ools.commands.walrus
+
-print >> sys.stderr, euca2ools.commands.Euca2ools().format_version()
+class NCInternalWalrus(euca2ools.commands.walrus.Walrus):
+ AUTH_CLASS = euca2ools.nc.auth.EucaRsaV2Auth
--
managing cloud instances for Eucalyptus
More information about the pkg-eucalyptus-commits
mailing list