~tribaal/txaws/xss-hardening

« back to all changes in this revision

Viewing changes to txaws/ec2/client.py

Merged 424018-add-keypair-support [r=jkakar] [f=424018]

This change implements three keypair methods in the EC2 client:
 1. describe_keypairs
 2. create_keypair
 3. delete_keypair

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
from txaws.util import iso8601time, XML
18
18
 
19
19
 
20
 
__all__ = ['EC2Client']
 
20
__all__ = ["EC2Client"]
21
21
 
22
22
 
23
23
class Reservation(object):
148
148
        self.progress = progress
149
149
 
150
150
 
 
151
class Keypair(object):
 
152
    """A convenience object for holding keypair data."""
 
153
 
 
154
    def __init__(self, name, fingerprint, material=None):
 
155
        self.name = name
 
156
        self.fingerprint = fingerprint
 
157
        self.material = material
 
158
 
 
159
 
151
160
class EC2Client(object):
152
161
    """A client for EC2."""
153
162
 
168
177
 
169
178
    def describe_instances(self):
170
179
        """Describe current instances."""
171
 
        q = self.query_factory('DescribeInstances', self.creds, self.endpoint)
 
180
        q = self.query_factory("DescribeInstances", self.creds, self.endpoint)
172
181
        d = q.submit()
173
182
        return d.addCallback(self._parse_instances)
174
183
 
245
254
        instanceset = {}
246
255
        for pos, instance_id in enumerate(instance_ids):
247
256
            instanceset["InstanceId.%d" % (pos+1)] = instance_id
248
 
        q = self.query_factory('TerminateInstances', self.creds, self.endpoint,
 
257
        q = self.query_factory("TerminateInstances", self.creds, self.endpoint,
249
258
                               instanceset)
250
259
        d = q.submit()
251
260
        return d.addCallback(self._parse_terminate_instances)
390
399
 
391
400
    def describe_snapshots(self, *snapshot_ids):
392
401
        """Describe available snapshots."""
393
 
        snapshotset = {}
 
402
        snapshot_set = {}
394
403
        for pos, snapshot_id in enumerate(snapshot_ids):
395
 
            snapshotset["SnapshotId.%d" % (pos + 1)] = snapshot_id
 
404
            snapshot_set["SnapshotId.%d" % (pos + 1)] = snapshot_id
396
405
        q = self.query_factory(
397
 
            "DescribeSnapshots", self.creds, self.endpoint, snapshotset)
 
406
            "DescribeSnapshots", self.creds, self.endpoint, snapshot_set)
398
407
        d = q.submit()
399
408
        return d.addCallback(self._parse_snapshots)
400
409
 
464
473
            attach_time[:19], "%Y-%m-%dT%H:%M:%S")
465
474
        return {"status": status, "attach_time": attach_time}
466
475
 
 
476
    def describe_keypairs(self, *keypair_names):
 
477
        """Returns information about key pairs available."""
 
478
        keypair_set = {}
 
479
        for pos, keypair_name in enumerate(keypair_names):
 
480
            keypair_set["KeyPair.%d" % (pos + 1)] = keypair_name
 
481
        q = self.query_factory("DescribeKeyPairs", self.creds, self.endpoint,
 
482
                               keypair_set)
 
483
        d = q.submit()
 
484
        return d.addCallback(self._parse_describe_keypairs)
 
485
 
 
486
    def _parse_describe_keypairs(self, xml_bytes):
 
487
        results = []
 
488
        root = XML(xml_bytes)
 
489
        for keypair_data in root.find("keySet"):
 
490
            key_name = keypair_data.findtext("keyName")
 
491
            key_fingerprint = keypair_data.findtext("keyFingerprint")
 
492
            results.append(Keypair(key_name, key_fingerprint))
 
493
        return results
 
494
 
 
495
    def create_keypair(self, keypair_name):
 
496
        """
 
497
        Create a new 2048 bit RSA key pair and return a unique ID that can be
 
498
        used to reference the created key pair when launching new instances.
 
499
        """
 
500
        q = self.query_factory(
 
501
            "CreateKeyPair", self.creds, self.endpoint,
 
502
            {"KeyName": keypair_name})
 
503
        d = q.submit()
 
504
        return d.addCallback(self._parse_create_keypair)
 
505
 
 
506
    def _parse_create_keypair(self, xml_bytes):
 
507
        results = []
 
508
        keypair_data = XML(xml_bytes)
 
509
        key_name = keypair_data.findtext("keyName")
 
510
        key_fingerprint = keypair_data.findtext("keyFingerprint")
 
511
        key_material = keypair_data.findtext("keyMaterial")
 
512
        return Keypair(key_name, key_fingerprint, key_material)
 
513
 
 
514
    def delete_keypair(self, keypair_name):
 
515
        """Delete a given keypair."""
 
516
        q = self.query_factory(
 
517
            "DeleteKeyPair", self.creds, self.endpoint,
 
518
            {"KeyName": keypair_name})
 
519
        d = q.submit()
 
520
        return d.addCallback(self._parse_delete_keypair)
 
521
 
 
522
    def _parse_delete_keypair(self, xml_bytes):
 
523
        results = []
 
524
        keypair_data = XML(xml_bytes)
 
525
        result = keypair_data.findtext("return")
 
526
        if not result:
 
527
            result = False
 
528
        elif result.lower() == "true":
 
529
            result = True
 
530
        else:
 
531
            result = False
 
532
        return result
 
533
 
467
534
 
468
535
class Query(object):
469
536
    """A query that may be submitted to EC2."""
477
544
        if api_version is None:
478
545
            api_version = version.aws_api
479
546
        self.params = {
480
 
            'Version': api_version,
481
 
            'SignatureVersion': '2',
482
 
            'SignatureMethod': 'HmacSHA1',
483
 
            'Action': action,
484
 
            'AWSAccessKeyId': self.creds.access_key,
485
 
            'Timestamp': iso8601time(time_tuple),
 
547
            "Version": api_version,
 
548
            "SignatureVersion": "2",
 
549
            "SignatureMethod": "HmacSHA1",
 
550
            "Action": action,
 
551
            "AWSAccessKeyId": self.creds.access_key,
 
552
            "Timestamp": iso8601time(time_tuple),
486
553
            }
487
554
        if other_params:
488
555
            self.params.update(other_params)
491
558
        """Return the canonical query params (used in signing)."""
492
559
        result = []
493
560
        for key, value in self.sorted_params():
494
 
            result.append('%s=%s' % (self.encode(key), self.encode(value)))
495
 
        return '&'.join(result)
 
561
            result.append("%s=%s" % (self.encode(key), self.encode(value)))
 
562
        return "&".join(result)
496
563
 
497
564
    def encode(self, a_string):
498
565
        """Encode a_string as per the canonicalisation encoding rules.
500
567
        See the AWS dev reference page 90 (2008-12-01 version).
501
568
        @return: a_string encoded.
502
569
        """
503
 
        return quote(a_string, safe='~')
 
570
        return quote(a_string, safe="~")
504
571
 
505
572
    def signing_text(self):
506
573
        """Return the text to be signed when signing the query."""
515
582
        submitting the query. Signing is done automatically - this is a public
516
583
        method to facilitate testing.
517
584
        """
518
 
        self.params['Signature'] = self.creds.sign(self.signing_text())
 
585
        self.params["Signature"] = self.creds.sign(self.signing_text())
519
586
 
520
587
    def sorted_params(self):
521
588
        """Return the query params sorted appropriately for signing."""