Merge lp:~smoser/ubuntu/natty/euca2ools/euca2ools-1.3.1 into lp:ubuntu/natty/euca2ools
- Natty (11.04)
- euca2ools-1.3.1
- Merge into natty
Proposed by
Scott Moser
on 2010-11-17
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Merged at revision: | 25 | ||||
| Proposed branch: | lp:~smoser/ubuntu/natty/euca2ools/euca2ools-1.3.1 | ||||
| Merge into: | lp:ubuntu/natty/euca2ools | ||||
| Diff against target: |
17095 lines (+10928/-2506) 134 files modified
.pc/.quilt_patches (+1/-0) .pc/.quilt_series (+1/-0) .pc/.version (+1/-0) .pc/applied-patches (+11/-0) .pc/bundle-image-support-symlinks.patch/euca2ools/euca2ools/__init__.py (+1458/-0) .pc/bundle-image-usage-on-invalid-user.patch/bin/euca-bundle-image (+285/-0) .pc/bundle-vol-exclude-persistent-udev-net-rules.patch/bin/euca-bundle-vol (+456/-0) .pc/bundle-vol-use-ec2-cert-by-default.patch/bin/euca-bundle-vol (+452/-0) .pc/describe-image-attribute-empty-kernel-ramdisk.patch/bin/euca-describe-image-attribute (+166/-0) .pc/describe-images-return-results-like-ec2-describe-images.patch/bin/euca-describe-images (+162/-0) .pc/download-bundle-fix-usage.patch/bin/euca-download-bundle (+189/-0) .pc/download-bundle-fix-usage.patch/man/euca-download-bundle.1 (+72/-0) .pc/euca-revoke-exit-on-usage.patch/bin/euca-revoke (+202/-0) .pc/print-instances-cleanup.patch/bin/euca-describe-instances (+156/-0) .pc/print-instances-cleanup.patch/bin/euca-run-instances (+260/-0) .pc/print-instances-cleanup.patch/euca2ools/euca2ools/__init__.py (+1458/-0) .pc/run-instances-error-not-trace-on-bad-instance-count.patch/bin/euca-run-instances (+256/-0) .pc/run-instances-usage-and-manpage-keypair.patch/bin/euca-run-instances (+256/-0) .pc/run-instances-usage-and-manpage-keypair.patch/man/euca-run-instances.1 (+67/-0) CHANGELOG (+17/-0) INSTALL (+7/-7) bin/euca-add-group (+33/-24) bin/euca-add-keypair (+28/-20) bin/euca-allocate-address (+21/-14) bin/euca-associate-address (+39/-30) bin/euca-attach-volume (+43/-36) bin/euca-authorize (+96/-83) bin/euca-bundle-image (+136/-91) bin/euca-bundle-instance (+205/-0) bin/euca-bundle-vol (+275/-200) bin/euca-cancel-bundle-task (+120/-0) bin/euca-confirm-product-instance (+42/-33) bin/euca-create-snapshot (+38/-26) bin/euca-create-volume (+60/-50) bin/euca-delete-bundle (+137/-113) bin/euca-delete-group (+25/-16) bin/euca-delete-keypair (+27/-19) bin/euca-delete-snapshot (+31/-23) bin/euca-delete-volume (+32/-25) bin/euca-deregister (+28/-22) bin/euca-describe-addresses (+30/-19) bin/euca-describe-availability-zones (+26/-16) bin/euca-describe-bundle-tasks (+118/-0) bin/euca-describe-groups (+42/-30) bin/euca-describe-image-attribute (+70/-53) bin/euca-describe-images (+51/-24) bin/euca-describe-instances (+40/-31) bin/euca-describe-keypairs (+26/-18) bin/euca-describe-regions (+26/-17) bin/euca-describe-snapshots (+36/-24) bin/euca-describe-volumes (+53/-37) bin/euca-detach-volume (+37/-28) bin/euca-disassociate-address (+27/-20) bin/euca-download-bundle (+72/-59) bin/euca-get-console-output (+35/-26) bin/euca-get-password (+126/-0) bin/euca-get-password-data (+106/-0) bin/euca-modify-image-attribute (+59/-46) bin/euca-reboot-instances (+29/-21) bin/euca-register (+94/-21) bin/euca-release-address (+29/-21) bin/euca-reset-image-attribute (+32/-24) bin/euca-revoke (+92/-77) bin/euca-run-instances (+145/-100) bin/euca-terminate-instances (+30/-21) bin/euca-unbundle (+77/-65) bin/euca-upload-bundle (+118/-87) bin/euca-version (+20/-13) debian/changelog (+7/-0) debian/patches/bundle-image-support-symlinks.patch (+17/-0) debian/patches/bundle-image-usage-on-invalid-user.patch (+54/-0) debian/patches/bundle-vol-exclude-persistent-udev-net-rules.patch (+31/-0) debian/patches/bundle-vol-use-ec2-cert-by-default.patch (+20/-0) debian/patches/describe-image-attribute-empty-kernel-ramdisk.patch (+26/-0) debian/patches/describe-images-return-results-like-ec2-describe-images.patch (+92/-0) debian/patches/download-bundle-fix-usage.patch (+39/-0) debian/patches/euca-revoke-exit-on-usage.patch (+19/-0) debian/patches/print-instances-cleanup.patch (+122/-0) debian/patches/run-instances-error-not-trace-on-bad-instance-count.patch (+33/-0) debian/patches/run-instances-usage-and-manpage-keypair.patch (+49/-0) debian/patches/series (+11/-0) debian/source/format (+1/-0) debian/watch (+1/-1) euca2ools.spec (+76/-42) euca2ools/euca2ools/__init__.py (+995/-639) euca2ools/setup.py (+21/-17) man/euca-add-group.1 (+1/-1) man/euca-add-keypair.1 (+1/-1) man/euca-allocate-address.1 (+1/-1) man/euca-associate-address.1 (+1/-1) man/euca-attach-volume.1 (+1/-1) man/euca-authorize.1 (+1/-1) man/euca-bundle-image.1 (+1/-1) man/euca-bundle-instance.1 (+46/-0) man/euca-bundle-vol.1 (+1/-1) man/euca-cancel-bundle-task.1 (+32/-0) man/euca-confirm-product-instance.1 (+1/-1) man/euca-create-snapshot.1 (+1/-1) man/euca-create-volume.1 (+2/-2) man/euca-delete-bundle.1 (+1/-1) man/euca-delete-group.1 (+1/-1) man/euca-delete-keypair.1 (+1/-1) man/euca-delete-snapshot.1 (+1/-1) man/euca-delete-volume.1 (+1/-1) man/euca-deregister.1 (+1/-1) man/euca-describe-addresses.1 (+1/-1) man/euca-describe-availability-zones.1 (+1/-1) man/euca-describe-bundle-tasks.1 (+33/-0) man/euca-describe-groups.1 (+1/-1) man/euca-describe-image-attribute.1 (+1/-1) man/euca-describe-images.1 (+1/-1) man/euca-describe-instances.1 (+1/-1) man/euca-describe-keypairs.1 (+1/-1) man/euca-describe-regions.1 (+1/-1) man/euca-describe-snapshots.1 (+1/-1) man/euca-describe-volumes.1 (+1/-1) man/euca-detach-volume.1 (+1/-1) man/euca-disassociate-address.1 (+1/-1) man/euca-download-bundle.1 (+3/-3) man/euca-get-console-output.1 (+1/-1) man/euca-get-password-data.1 (+32/-0) man/euca-get-password.1 (+34/-0) man/euca-modify-image-attribute.1 (+6/-5) man/euca-reboot-instances.1 (+1/-1) man/euca-register.1 (+20/-4) man/euca-release-address.1 (+1/-1) man/euca-reset-image-attribute.1 (+2/-2) man/euca-revoke.1 (+1/-1) man/euca-run-instances.1 (+34/-23) man/euca-terminate-instances.1 (+1/-1) man/euca-unbundle.1 (+1/-1) man/euca-upload-bundle.1 (+1/-1) man/euca-version.1 (+2/-2) util/euca2ools (+38/-1) |
||||
| To merge this branch: | bzr merge lp:~smoser/ubuntu/natty/euca2ools/euca2ools-1.3.1 | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Dave Walker | 2010-11-17 | Approve on 2010-11-17 | |
| Dustin Kirkland | 2010-11-17 | Pending | |
| Ubuntu branches | 2010-11-17 | Pending | |
|
Review via email:
|
|||
Commit Message
Description of the Change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === added directory '.pc' |
| 2 | === added file '.pc/.quilt_patches' |
| 3 | --- .pc/.quilt_patches 1970-01-01 00:00:00 +0000 |
| 4 | +++ .pc/.quilt_patches 2010-11-17 22:00:42 +0000 |
| 5 | @@ -0,0 +1,1 @@ |
| 6 | +debian/patches |
| 7 | |
| 8 | === added file '.pc/.quilt_series' |
| 9 | --- .pc/.quilt_series 1970-01-01 00:00:00 +0000 |
| 10 | +++ .pc/.quilt_series 2010-11-17 22:00:42 +0000 |
| 11 | @@ -0,0 +1,1 @@ |
| 12 | +series |
| 13 | |
| 14 | === added file '.pc/.version' |
| 15 | --- .pc/.version 1970-01-01 00:00:00 +0000 |
| 16 | +++ .pc/.version 2010-11-17 22:00:42 +0000 |
| 17 | @@ -0,0 +1,1 @@ |
| 18 | +2 |
| 19 | |
| 20 | === added file '.pc/applied-patches' |
| 21 | --- .pc/applied-patches 1970-01-01 00:00:00 +0000 |
| 22 | +++ .pc/applied-patches 2010-11-17 22:00:42 +0000 |
| 23 | @@ -0,0 +1,11 @@ |
| 24 | +bundle-vol-use-ec2-cert-by-default.patch |
| 25 | +bundle-image-support-symlinks.patch |
| 26 | +describe-images-return-results-like-ec2-describe-images.patch |
| 27 | +run-instances-usage-and-manpage-keypair.patch |
| 28 | +run-instances-error-not-trace-on-bad-instance-count.patch |
| 29 | +print-instances-cleanup.patch |
| 30 | +euca-revoke-exit-on-usage.patch |
| 31 | +download-bundle-fix-usage.patch |
| 32 | +bundle-image-usage-on-invalid-user.patch |
| 33 | +describe-image-attribute-empty-kernel-ramdisk.patch |
| 34 | +bundle-vol-exclude-persistent-udev-net-rules.patch |
| 35 | |
| 36 | === added directory '.pc/bundle-image-support-symlinks.patch' |
| 37 | === added file '.pc/bundle-image-support-symlinks.patch/.timestamp' |
| 38 | === added directory '.pc/bundle-image-support-symlinks.patch/euca2ools' |
| 39 | === added directory '.pc/bundle-image-support-symlinks.patch/euca2ools/euca2ools' |
| 40 | === added file '.pc/bundle-image-support-symlinks.patch/euca2ools/euca2ools/__init__.py' |
| 41 | --- .pc/bundle-image-support-symlinks.patch/euca2ools/euca2ools/__init__.py 1970-01-01 00:00:00 +0000 |
| 42 | +++ .pc/bundle-image-support-symlinks.patch/euca2ools/euca2ools/__init__.py 2010-11-17 22:00:42 +0000 |
| 43 | @@ -0,0 +1,1458 @@ |
| 44 | +#!/usr/bin/python |
| 45 | +# -*- coding: utf-8 -*- |
| 46 | +# Software License Agreement (BSD License) |
| 47 | +# |
| 48 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 49 | +# All rights reserved. |
| 50 | +# |
| 51 | +# Redistribution and use of this software in source and binary forms, with or |
| 52 | +# without modification, are permitted provided that the following conditions |
| 53 | +# are met: |
| 54 | +# |
| 55 | +# Redistributions of source code must retain the above |
| 56 | +# copyright notice, this list of conditions and the |
| 57 | +# following disclaimer. |
| 58 | +# |
| 59 | +# Redistributions in binary form must reproduce the above |
| 60 | +# copyright notice, this list of conditions and the |
| 61 | +# following disclaimer in the documentation and/or other |
| 62 | +# materials provided with the distribution. |
| 63 | +# |
| 64 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 65 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 66 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 67 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 68 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 69 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 70 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 71 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 72 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 73 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 74 | +# POSSIBILITY OF SUCH DAMAGE. |
| 75 | +# |
| 76 | +# Author: Neil Soman neil@eucalyptus.com |
| 77 | + |
| 78 | +import boto |
| 79 | +import getopt |
| 80 | +import sys |
| 81 | +import os |
| 82 | +import tarfile |
| 83 | +from xml.dom.minidom import Document |
| 84 | +from xml.dom import minidom |
| 85 | +from hashlib import sha1 as sha |
| 86 | +from M2Crypto import BN, EVP, RSA, X509 |
| 87 | +from binascii import hexlify, unhexlify |
| 88 | +from subprocess import * |
| 89 | +import platform |
| 90 | +import urllib |
| 91 | +import re |
| 92 | +import shutil |
| 93 | +from boto.ec2.regioninfo import RegionInfo |
| 94 | +from boto.ec2.blockdevicemapping import BlockDeviceMapping |
| 95 | +from boto.ec2.blockdevicemapping import EBSBlockDeviceType |
| 96 | +from boto.ec2.connection import EC2Connection |
| 97 | +from boto.resultset import ResultSet |
| 98 | +import logging |
| 99 | +import base64 |
| 100 | + |
| 101 | +BUNDLER_NAME = 'euca-tools' |
| 102 | +BUNDLER_VERSION = '1.3' |
| 103 | +VERSION = '2007-10-10' |
| 104 | +RELEASE = '31337' |
| 105 | +AES = 'AES-128-CBC' |
| 106 | + |
| 107 | +IP_PROTOCOLS = ['tcp', 'udp', 'icmp'] |
| 108 | + |
| 109 | +IMAGE_IO_CHUNK = 10 * 1024 |
| 110 | +IMAGE_SPLIT_CHUNK = IMAGE_IO_CHUNK * 1024 |
| 111 | +MAX_LOOP_DEVS = 256 |
| 112 | + |
| 113 | +METADATA_URL = 'http://169.254.169.254/latest/meta-data/' |
| 114 | + |
| 115 | +# |
| 116 | +# Monkey patch the SAX endElement handler in boto's |
| 117 | +# BlockDeviceMapping class to not break on Eucalyptus's |
| 118 | +# DescribeImageAttribute output. It's impossible to test |
| 119 | +# this against EC2 because EC2 will not let you run a |
| 120 | +# DescribeImageAttribute on the blockDeviceMapping attribute, |
| 121 | +# even for an image you own. |
| 122 | +# |
| 123 | +def endElement(self, name, value, connection): |
| 124 | + if name == 'virtualName': |
| 125 | + self.current_vname = value |
| 126 | + elif name == 'device' or name == 'deviceName': |
| 127 | + if hasattr(self, 'current_vname') and self.current_vname: |
| 128 | + self[self.current_vname] = value |
| 129 | + self.current_vname = None |
| 130 | + else: |
| 131 | + self.current_name = value |
| 132 | + |
| 133 | +BlockDeviceMapping.endElement = endElement |
| 134 | + |
| 135 | +# |
| 136 | +# Monkey patch the register_image method from EC2Connection |
| 137 | +# The one in 1.9b required a value for the "name" parameter |
| 138 | +# but in fact that parameter is only required for EBS-backed |
| 139 | +# images. So, an EBS image needs a name but not a location |
| 140 | +# and the S3 image needs a location but not a name. |
| 141 | +# |
| 142 | +def register_image(self, name=None, description=None, image_location=None, |
| 143 | + architecture=None, kernel_id=None, ramdisk_id=None, |
| 144 | + root_device_name=None, block_device_map=None): |
| 145 | + """ |
| 146 | + Register an image. |
| 147 | + |
| 148 | + :type name: string |
| 149 | + :param name: The name of the AMI. Valid only for EBS-based images. |
| 150 | + |
| 151 | + :type description: string |
| 152 | + :param description: The description of the AMI. |
| 153 | + |
| 154 | + :type image_location: string |
| 155 | + :param image_location: Full path to your AMI manifest in Amazon S3 storage. |
| 156 | + Only used for S3-based AMI's. |
| 157 | + |
| 158 | + :type architecture: string |
| 159 | + :param architecture: The architecture of the AMI. Valid choices are: |
| 160 | + i386 | x86_64 |
| 161 | + |
| 162 | + :type kernel_id: string |
| 163 | + :param kernel_id: The ID of the kernel with which to launch the instances |
| 164 | + |
| 165 | + :type root_device_name: string |
| 166 | + :param root_device_name: The root device name (e.g. /dev/sdh) |
| 167 | + |
| 168 | + :type block_device_map: :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping` |
| 169 | + :param block_device_map: A BlockDeviceMapping data structure |
| 170 | + describing the EBS volumes associated |
| 171 | + with the Image. |
| 172 | + |
| 173 | + :rtype: string |
| 174 | + :return: The new image id |
| 175 | + """ |
| 176 | + params = {} |
| 177 | + if name: |
| 178 | + params['Name'] = name |
| 179 | + if description: |
| 180 | + params['Description'] = description |
| 181 | + if architecture: |
| 182 | + params['Architecture'] = architecture |
| 183 | + if kernel_id: |
| 184 | + params['KernelId'] = kernel_id |
| 185 | + if ramdisk_id: |
| 186 | + params['RamdiskId'] = ramdisk_id |
| 187 | + if image_location: |
| 188 | + params['ImageLocation'] = image_location |
| 189 | + if root_device_name: |
| 190 | + params['RootDeviceName'] = root_device_name |
| 191 | + if block_device_map: |
| 192 | + block_device_map.build_list_params(params) |
| 193 | + rs = self.get_object('RegisterImage', params, ResultSet) |
| 194 | + image_id = getattr(rs, 'imageId', None) |
| 195 | + return image_id |
| 196 | + |
| 197 | +EC2Connection.register_image = register_image |
| 198 | + |
| 199 | +class LinuxImage: |
| 200 | + |
| 201 | + ALLOWED_FS_TYPES = ['ext2', 'ext3', 'xfs', 'jfs', 'reiserfs'] |
| 202 | + BANNED_MOUNTS = [ |
| 203 | + '/dev', |
| 204 | + '/media', |
| 205 | + '/mnt', |
| 206 | + '/proc', |
| 207 | + '/sys', |
| 208 | + '/cdrom', |
| 209 | + '/tmp', |
| 210 | + ] |
| 211 | + ESSENTIAL_DIRS = ['proc', 'tmp', 'dev', 'mnt', 'sys'] |
| 212 | + ESSENTIAL_DEVS = [ |
| 213 | + [os.path.join('dev', 'console'), 'c', '5', '1'], |
| 214 | + [os.path.join('dev', 'full'), 'c', '1', '7'], |
| 215 | + [os.path.join('dev', 'null'), 'c', '1', '3'], |
| 216 | + [os.path.join('dev', 'zero'), 'c', '1', '5'], |
| 217 | + [os.path.join('dev', 'tty'), 'c', '5', '0'], |
| 218 | + [os.path.join('dev', 'tty0'), 'c', '4', '0'], |
| 219 | + [os.path.join('dev', 'tty1'), 'c', '4', '1'], |
| 220 | + [os.path.join('dev', 'tty2'), 'c', '4', '2'], |
| 221 | + [os.path.join('dev', 'tty3'), 'c', '4', '3'], |
| 222 | + [os.path.join('dev', 'tty4'), 'c', '4', '4'], |
| 223 | + [os.path.join('dev', 'tty5'), 'c', '4', '5'], |
| 224 | + [os.path.join('dev', 'xvc0'), 'c', '204', '191'], |
| 225 | + ] |
| 226 | + MAKEFS_CMD = 'mkfs.ext3' |
| 227 | + NEW_FSTAB = \ |
| 228 | + """ |
| 229 | +/dev/sda1 / ext3 defaults 1 1 |
| 230 | +/dev/sdb /mnt ext3 defaults 0 0 |
| 231 | +none /dev/pts devpts gid=5,mode=620 0 0 |
| 232 | +none /proc proc defaults 0 0 |
| 233 | +none /sys sysfs defaults 0 0 |
| 234 | + """ |
| 235 | + |
| 236 | + OLD_FSTAB = \ |
| 237 | + """/dev/sda1 / ext3 defaults,errors=remount-ro 0 0 |
| 238 | +/dev/sda2 /mnt ext3 defaults 0 0 |
| 239 | +/dev/sda3 swap swap defaults 0 0 |
| 240 | +proc /proc proc defaults 0 0 |
| 241 | +devpts /dev/pts devpts gid=5,mode=620 0 0""" |
| 242 | + |
| 243 | + def __init__(self, debug=False): |
| 244 | + self.debug = debug |
| 245 | + |
| 246 | + def create_image(self, size_in_MB, image_path): |
| 247 | + dd_cmd = ['dd'] |
| 248 | + dd_cmd.append('if=/dev/zero') |
| 249 | + dd_cmd.append('of=%s' % image_path) |
| 250 | + dd_cmd.append('count=1') |
| 251 | + dd_cmd.append('bs=1M') |
| 252 | + dd_cmd.append('seek=%s' % (size_in_MB - 1)) |
| 253 | + if self.debug: |
| 254 | + print 'Creating disk image...', image_path |
| 255 | + Popen(dd_cmd, PIPE).communicate()[0] |
| 256 | + |
| 257 | + def make_fs(self, image_path): |
| 258 | + Util().check_prerequisite_command(self.MAKEFS_CMD) |
| 259 | + |
| 260 | + if self.debug: |
| 261 | + print 'Creating filesystem...' |
| 262 | + makefs_cmd = Popen([self.MAKEFS_CMD, '-F', image_path], |
| 263 | + PIPE).communicate()[0] |
| 264 | + |
| 265 | + def add_fstab( |
| 266 | + self, |
| 267 | + mount_point, |
| 268 | + generate_fstab, |
| 269 | + fstab_path, |
| 270 | + ): |
| 271 | + if not fstab_path: |
| 272 | + return |
| 273 | + fstab = None |
| 274 | + if fstab_path == 'old': |
| 275 | + if not generate_fstab: |
| 276 | + return |
| 277 | + fstab = self.OLD_FSTAB |
| 278 | + elif fstab_path == 'new': |
| 279 | + if not generate_fstab: |
| 280 | + return |
| 281 | + fstab = self.NEW_FSTAB |
| 282 | + |
| 283 | + etc_file_path = os.path.join(mount_point, 'etc') |
| 284 | + fstab_file_path = os.path.join(etc_file_path, 'fstab') |
| 285 | + if not os.path.exists(etc_file_path): |
| 286 | + os.mkdir(etc_file_path) |
| 287 | + else: |
| 288 | + if os.path.exists(fstab_file_path): |
| 289 | + fstab_copy_path = fstab_file_path + '.old' |
| 290 | + shutil.copyfile(fstab_file_path, fstab_copy_path) |
| 291 | + |
| 292 | + if self.debug: |
| 293 | + print 'Updating fstab entry' |
| 294 | + fstab_file = open(fstab_file_path, 'w') |
| 295 | + if fstab: |
| 296 | + fstab_file.write(fstab) |
| 297 | + else: |
| 298 | + orig_fstab_file = open(fstab_path, 'r') |
| 299 | + while 1: |
| 300 | + data = orig_fstab_file.read(IMAGE_IO_CHUNK) |
| 301 | + if not data: |
| 302 | + break |
| 303 | + fstab_file.write(data) |
| 304 | + orig_fstab_file.close() |
| 305 | + fstab_file.close() |
| 306 | + |
| 307 | + def make_essential_devs(self, image_path): |
| 308 | + for entry in self.ESSENTIAL_DEVS: |
| 309 | + cmd = ['mknod'] |
| 310 | + entry[0] = os.path.join(image_path, entry[0]) |
| 311 | + cmd.extend(entry) |
| 312 | + Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
| 313 | + |
| 314 | + |
| 315 | +class SolarisImage: |
| 316 | + |
| 317 | + ALLOWED_FS_TYPES = ['ext2', 'ext3', 'xfs', 'jfs', 'reiserfs'] |
| 318 | + BANNED_MOUNTS = [ |
| 319 | + '/dev', |
| 320 | + '/media', |
| 321 | + '/mnt', |
| 322 | + '/proc', |
| 323 | + '/sys', |
| 324 | + '/cdrom', |
| 325 | + '/tmp', |
| 326 | + ] |
| 327 | + ESSENTIAL_DIRS = ['proc', 'tmp', 'dev', 'mnt', 'sys'] |
| 328 | + |
| 329 | + def __init__(self, debug=False): |
| 330 | + self.debug = debug |
| 331 | + |
| 332 | + def create_image(self, size_in_MB, image_path): |
| 333 | + print 'Sorry. Solaris not supported yet' |
| 334 | + raise UnsupportedException |
| 335 | + |
| 336 | + def make_fs(self, image_path): |
| 337 | + print 'Sorry. Solaris not supported yet' |
| 338 | + raise UnsupportedException |
| 339 | + |
| 340 | + def make_essential_devs(self, image_path): |
| 341 | + print 'Sorry. Solaris not supported yet' |
| 342 | + raise UnsupportedException |
| 343 | + |
| 344 | + |
| 345 | +class Util: |
| 346 | + |
| 347 | + usage_string = \ |
| 348 | + """ |
| 349 | +-a, --access-key User's Access Key ID. |
| 350 | + |
| 351 | +-s, --secret-key User's Secret Key. |
| 352 | + |
| 353 | +-U, --url URL of the Cloud to connect to. |
| 354 | + |
| 355 | +--config Read credentials and cloud settings from the |
| 356 | + specified config file (defaults to $HOME/.eucarc or /etc/euca2ools/eucarc). |
| 357 | + |
| 358 | +-h, --help Display this help message. |
| 359 | + |
| 360 | +--version Display the version of this tool. |
| 361 | + |
| 362 | +--debug Turn on debugging. |
| 363 | + |
| 364 | +Euca2ools will use the environment variables EC2_URL, EC2_ACCESS_KEY, EC2_SECRET_KEY, EC2_CERT, EC2_PRIVATE_KEY, S3_URL, EUCALYPTUS_CERT by default. |
| 365 | + """ |
| 366 | + |
| 367 | + version_string = """ Version: 1.2 (BSD)""" |
| 368 | + |
| 369 | + def version(self): |
| 370 | + return self.version_string |
| 371 | + |
| 372 | + def usage(self, compat=False): |
| 373 | + if compat: |
| 374 | + self.usage_string = self.usage_string.replace('-s,', '-S,') |
| 375 | + self.usage_string = self.usage_string.replace('-a,', '-A,') |
| 376 | + print self.usage_string |
| 377 | + |
| 378 | + def check_prerequisite_command(self, command): |
| 379 | + cmd = [command] |
| 380 | + try: |
| 381 | + output = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
| 382 | + except OSError, e: |
| 383 | + error_string = '%s' % e |
| 384 | + if 'No such' in error_string: |
| 385 | + print 'Command %s not found. Is it installed?' % command |
| 386 | + raise NotFoundError |
| 387 | + else: |
| 388 | + raise OSError(e) |
| 389 | + |
| 390 | + |
| 391 | +class AddressValidationError: |
| 392 | + |
| 393 | + def __init__(self): |
| 394 | + self.message = 'Invalid address' |
| 395 | + |
| 396 | + |
| 397 | +class InstanceValidationError: |
| 398 | + |
| 399 | + def __init__(self): |
| 400 | + self.message = 'Invalid instance id' |
| 401 | + |
| 402 | + |
| 403 | +class VolumeValidationError: |
| 404 | + |
| 405 | + def __init__(self): |
| 406 | + self.message = 'Invalid volume id' |
| 407 | + |
| 408 | + |
| 409 | +class SizeValidationError: |
| 410 | + |
| 411 | + def __init__(self): |
| 412 | + self.message = 'Invalid size' |
| 413 | + |
| 414 | + |
| 415 | +class SnapshotValidationError: |
| 416 | + |
| 417 | + def __init__(self): |
| 418 | + self.message = 'Invalid snapshot id' |
| 419 | + |
| 420 | + |
| 421 | +class ProtocolValidationError: |
| 422 | + |
| 423 | + def __init__(self): |
| 424 | + self.message = 'Invalid protocol' |
| 425 | + |
| 426 | + |
| 427 | +class FileValidationError: |
| 428 | + |
| 429 | + def __init__(self): |
| 430 | + self.message = 'Invalid file' |
| 431 | + |
| 432 | + |
| 433 | +class DirValidationError: |
| 434 | + |
| 435 | + def __init__(self): |
| 436 | + self.message = 'Invalid directory' |
| 437 | + |
| 438 | + |
| 439 | +class BundleValidationError: |
| 440 | + |
| 441 | + def __init__(self): |
| 442 | + self.message = 'Invalid bundle id' |
| 443 | + |
| 444 | + |
| 445 | +class CopyError: |
| 446 | + |
| 447 | + def __init__(self): |
| 448 | + self.message = 'Unable to copy' |
| 449 | + |
| 450 | + |
| 451 | +class MetadataReadError: |
| 452 | + |
| 453 | + def __init__(self): |
| 454 | + self.message = 'Unable to read metadata' |
| 455 | + |
| 456 | + |
| 457 | +class NullHandler(logging.Handler): |
| 458 | + |
| 459 | + def emit(self, record): |
| 460 | + pass |
| 461 | + |
| 462 | + |
| 463 | +class NotFoundError: |
| 464 | + |
| 465 | + def __init__(self): |
| 466 | + self.message = 'Unable to find' |
| 467 | + |
| 468 | + |
| 469 | +class UnsupportedException: |
| 470 | + |
| 471 | + def __init__(self): |
| 472 | + self.message = 'Not supported' |
| 473 | + |
| 474 | + |
| 475 | +class CommandFailed: |
| 476 | + |
| 477 | + def __init__(self): |
| 478 | + self.message = 'Command failed' |
| 479 | + |
| 480 | + |
| 481 | +class ConnectionFailed: |
| 482 | + |
| 483 | + def __init__(self): |
| 484 | + self.message = 'Connection failed' |
| 485 | + |
| 486 | + |
| 487 | +class ParseError: |
| 488 | + |
| 489 | + def __init__(self, msg): |
| 490 | + self.message = msg |
| 491 | + |
| 492 | + |
| 493 | +class Euca2ool: |
| 494 | + |
| 495 | + def process_args(self): |
| 496 | + ids = [] |
| 497 | + for arg in self.args: |
| 498 | + ids.append(arg) |
| 499 | + return ids |
| 500 | + |
| 501 | + def __init__( |
| 502 | + self, |
| 503 | + short_opts=None, |
| 504 | + long_opts=None, |
| 505 | + is_s3=False, |
| 506 | + compat=False, |
| 507 | + ): |
| 508 | + self.ec2_user_access_key = None |
| 509 | + self.ec2_user_secret_key = None |
| 510 | + self.ec2_url = None |
| 511 | + self.s3_url = None |
| 512 | + self.config_file_path = None |
| 513 | + self.is_s3 = is_s3 |
| 514 | + if compat: |
| 515 | + self.secret_key_opt = 'S' |
| 516 | + self.access_key_opt = 'A' |
| 517 | + else: |
| 518 | + self.secret_key_opt = 's' |
| 519 | + self.access_key_opt = 'a' |
| 520 | + if not short_opts: |
| 521 | + short_opts = '' |
| 522 | + if not long_opts: |
| 523 | + long_opts = [''] |
| 524 | + short_opts += 'hU:' |
| 525 | + short_opts += '%s:' % self.secret_key_opt |
| 526 | + short_opts += '%s:' % self.access_key_opt |
| 527 | + long_opts += [ |
| 528 | + 'access-key=', |
| 529 | + 'secret-key=', |
| 530 | + 'url=', |
| 531 | + 'help', |
| 532 | + 'version', |
| 533 | + 'debug', |
| 534 | + 'config=', |
| 535 | + ] |
| 536 | + (opts, args) = getopt.gnu_getopt(sys.argv[1:], short_opts, |
| 537 | + long_opts) |
| 538 | + self.opts = opts |
| 539 | + self.args = args |
| 540 | + self.debug = False |
| 541 | + for (name, value) in opts: |
| 542 | + if name in ('-%s' % self.access_key_opt, '--access-key'): |
| 543 | + self.ec2_user_access_key = value |
| 544 | + elif name in ('-%s' % self.secret_key_opt, '--secret-key'): |
| 545 | + try: |
| 546 | + self.ec2_user_secret_key = int(value) |
| 547 | + self.ec2_user_secret_key = None |
| 548 | + except ValueError: |
| 549 | + self.ec2_user_secret_key = value |
| 550 | + elif name in ('-U', '--url'): |
| 551 | + self.ec2_url = value |
| 552 | + elif name == '--debug': |
| 553 | + self.debug = True |
| 554 | + elif name == '--config': |
| 555 | + self.config_file_path = value |
| 556 | + system_string = platform.system() |
| 557 | + if system_string == 'Linux': |
| 558 | + self.img = LinuxImage(self.debug) |
| 559 | + elif system_string == 'SunOS': |
| 560 | + self.img = SolarisImage(self.debug) |
| 561 | + else: |
| 562 | + self.img = 'Unsupported' |
| 563 | + self.setup_environ() |
| 564 | + |
| 565 | + h = NullHandler() |
| 566 | + logging.getLogger('boto').addHandler(h) |
| 567 | + |
| 568 | + SYSTEM_EUCARC_PATH = os.path.join('/etc', 'euca2ools', 'eucarc') |
| 569 | + |
| 570 | + def setup_environ(self): |
| 571 | + envlist = ( |
| 572 | + 'EC2_ACCESS_KEY', |
| 573 | + 'EC2_SECRET_KEY', |
| 574 | + 'S3_URL', |
| 575 | + 'EC2_URL', |
| 576 | + 'EC2_CERT', |
| 577 | + 'EC2_PRIVATE_KEY', |
| 578 | + 'EUCALYPTUS_CERT', |
| 579 | + 'EC2_USER_ID', |
| 580 | + ) |
| 581 | + self.environ = {} |
| 582 | + user_eucarc = None |
| 583 | + if 'HOME' in os.environ: |
| 584 | + user_eucarc = os.path.join(os.getenv('HOME'), '.eucarc') |
| 585 | + read_config = False |
| 586 | + if self.config_file_path \ |
| 587 | + and os.path.exists(self.config_file_path): |
| 588 | + read_config = self.config_file_path |
| 589 | + elif user_eucarc is not None and os.path.exists(user_eucarc): |
| 590 | + read_config = user_eucarc |
| 591 | + elif os.path.exists(self.SYSTEM_EUCARC_PATH): |
| 592 | + read_config = self.SYSTEM_EUCARC_PATH |
| 593 | + if read_config: |
| 594 | + parse_config(read_config, self.environ, envlist) |
| 595 | + else: |
| 596 | + for v in envlist: |
| 597 | + self.environ[v] = os.getenv(v) |
| 598 | + |
| 599 | + def get_environ(self, name): |
| 600 | + if self.environ.has_key(name): |
| 601 | + return self.environ[name] |
| 602 | + else: |
| 603 | + print '%s not found' % name |
| 604 | + raise NotFoundError |
| 605 | + |
| 606 | + def make_connection(self): |
| 607 | + if not self.ec2_user_access_key: |
| 608 | + self.ec2_user_access_key = self.environ['EC2_ACCESS_KEY'] |
| 609 | + if not self.ec2_user_access_key: |
| 610 | + print 'EC2_ACCESS_KEY environment variable must be set.' |
| 611 | + raise ConnectionFailed |
| 612 | + |
| 613 | + if not self.ec2_user_secret_key: |
| 614 | + self.ec2_user_secret_key = self.environ['EC2_SECRET_KEY'] |
| 615 | + if not self.ec2_user_secret_key: |
| 616 | + print 'EC2_SECRET_KEY environment variable must be set.' |
| 617 | + raise ConnectionFailed |
| 618 | + |
| 619 | + if not self.is_s3: |
| 620 | + if not self.ec2_url: |
| 621 | + self.ec2_url = self.environ['EC2_URL'] |
| 622 | + if not self.ec2_url: |
| 623 | + self.ec2_url = \ |
| 624 | + 'http://localhost:8773/services/Eucalyptus' |
| 625 | + print 'EC2_URL not specified. Trying %s' \ |
| 626 | + % self.ec2_url |
| 627 | + else: |
| 628 | + if not self.ec2_url: |
| 629 | + self.ec2_url = self.environ['S3_URL'] |
| 630 | + if not self.ec2_url: |
| 631 | + self.ec2_url = \ |
| 632 | + 'http://localhost:8773/services/Walrus' |
| 633 | + print 'S3_URL not specified. Trying %s' \ |
| 634 | + % self.ec2_url |
| 635 | + |
| 636 | + self.port = None |
| 637 | + self.service_path = '/' |
| 638 | + if self.ec2_url.find('https://') >= 0: |
| 639 | + self.ec2_url = self.ec2_url.replace('https://', '') |
| 640 | + self.is_secure = True |
| 641 | + else: |
| 642 | + self.ec2_url = self.ec2_url.replace('http://', '') |
| 643 | + self.is_secure = False |
| 644 | + self.host = self.ec2_url |
| 645 | + url_parts = self.ec2_url.split(':') |
| 646 | + if len(url_parts) > 1: |
| 647 | + self.host = url_parts[0] |
| 648 | + path_parts = url_parts[1].split('/', 1) |
| 649 | + if len(path_parts) > 1: |
| 650 | + self.port = int(path_parts[0]) |
| 651 | + self.service_path = self.service_path + path_parts[1] |
| 652 | + else: |
| 653 | + self.port = int(url_parts[1]) |
| 654 | + |
| 655 | + if not self.is_s3: |
| 656 | + return EC2Connection( |
| 657 | + aws_access_key_id=self.ec2_user_access_key, |
| 658 | + aws_secret_access_key=self.ec2_user_secret_key, |
| 659 | + is_secure=self.is_secure, |
| 660 | + region=RegionInfo(None, 'eucalyptus', self.host), |
| 661 | + port=self.port, |
| 662 | + path=self.service_path, |
| 663 | + ) |
| 664 | + else: |
| 665 | + return boto.s3.Connection( |
| 666 | + aws_access_key_id=self.ec2_user_access_key, |
| 667 | + aws_secret_access_key=self.ec2_user_secret_key, |
| 668 | + is_secure=self.is_secure, |
| 669 | + host=self.host, |
| 670 | + port=self.port, |
| 671 | + calling_format=boto.s3.connection.OrdinaryCallingFormat(), |
| 672 | + path=self.service_path, |
| 673 | + ) |
| 674 | + |
| 675 | + def validate_address(self, address): |
| 676 | + if not re.match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(\/[0-9]+)?$", |
| 677 | + address): |
| 678 | + raise AddressValidationError |
| 679 | + |
| 680 | + def validate_instance_id(self, id): |
| 681 | + if not re.match('i-', id): |
| 682 | + raise InstanceValidationError |
| 683 | + |
| 684 | + def validate_volume_id(self, id): |
| 685 | + if not re.match('vol-', id): |
| 686 | + raise VolumeValidationError |
| 687 | + |
| 688 | + def validate_volume_size(self, size): |
| 689 | + if size < 0 or size > 1024: |
| 690 | + raise SizeValidationError |
| 691 | + |
| 692 | + def validate_snapshot_id(self, id): |
| 693 | + if not re.match('snap-', id): |
| 694 | + raise SnapshotValidationError |
| 695 | + |
| 696 | + def validate_protocol(self, proto): |
| 697 | + if not proto in IP_PROTOCOLS: |
| 698 | + raise ProtocolValidationError |
| 699 | + |
| 700 | + def validate_file(self, path): |
| 701 | + if not os.path.exists(path) or not os.path.isfile(path): |
| 702 | + raise FileValidationError |
| 703 | + |
| 704 | + def validate_dir(self, path): |
| 705 | + if not os.path.exists(path) or not os.path.isdir(path): |
| 706 | + raise DirValidationError |
| 707 | + |
| 708 | + def validate_bundle_id(self, id): |
| 709 | + if not re.match('bun-', id): |
| 710 | + raise BundleValidationError |
| 711 | + |
| 712 | + def get_relative_filename(self, filename): |
| 713 | + f_parts = filename.split('/') |
| 714 | + return f_parts[len(f_parts) - 1] |
| 715 | + |
| 716 | + def get_file_path(self, filename): |
| 717 | + relative_filename = self.get_relative_filename(filename) |
| 718 | + file_path = os.path.dirname(filename) |
| 719 | + if len(file_path) == 0: |
| 720 | + file_path = '.' |
| 721 | + return file_path |
| 722 | + |
| 723 | + def split_file(self, file, chunk_size): |
| 724 | + parts = [] |
| 725 | + parts_digest = [] |
| 726 | + file_size = os.path.getsize(file) |
| 727 | + in_file = open(file, 'rb') |
| 728 | + number_parts = int(file_size / chunk_size) |
| 729 | + number_parts += 1 |
| 730 | + bytes_read = 0 |
| 731 | + for i in range(0, number_parts, 1): |
| 732 | + filename = '%s.%d' % (file, i) |
| 733 | + part_digest = sha() |
| 734 | + file_part = open(filename, 'wb') |
| 735 | + print 'Part:', self.get_relative_filename(filename) |
| 736 | + part_bytes_written = 0 |
| 737 | + while part_bytes_written < IMAGE_SPLIT_CHUNK: |
| 738 | + data = in_file.read(IMAGE_IO_CHUNK) |
| 739 | + file_part.write(data) |
| 740 | + part_digest.update(data) |
| 741 | + data_len = len(data) |
| 742 | + part_bytes_written += data_len |
| 743 | + bytes_read += data_len |
| 744 | + if bytes_read >= file_size: |
| 745 | + break |
| 746 | + file_part.close() |
| 747 | + parts.append(filename) |
| 748 | + parts_digest.append(hexlify(part_digest.digest())) |
| 749 | + |
| 750 | + in_file.close() |
| 751 | + return (parts, parts_digest) |
| 752 | + |
| 753 | + def check_image(self, image_file, path): |
| 754 | + print 'Checking image' |
| 755 | + if not os.path.exists(path): |
| 756 | + os.makedirs(path) |
| 757 | + image_size = os.path.getsize(image_file) |
| 758 | + if self.debug: |
| 759 | + print 'Image Size:', image_size, 'bytes' |
| 760 | + in_file = open(image_file, 'rb') |
| 761 | + sha_image = sha() |
| 762 | + while 1: |
| 763 | + buf = in_file.read(IMAGE_IO_CHUNK) |
| 764 | + if not buf: |
| 765 | + break |
| 766 | + sha_image.update(buf) |
| 767 | + return (image_size, hexlify(sha_image.digest())) |
| 768 | + |
| 769 | + def tarzip_image( |
| 770 | + self, |
| 771 | + prefix, |
| 772 | + file, |
| 773 | + path, |
| 774 | + ): |
| 775 | + Util().check_prerequisite_command('tar') |
| 776 | + |
| 777 | + print 'Tarring image' |
| 778 | + tar_file = '%s.tar.gz' % os.path.join(path, prefix) |
| 779 | + outfile = open(tar_file, 'wb') |
| 780 | + file_path = self.get_file_path(file) |
| 781 | + tar_cmd = ['tar', 'c', '-S'] |
| 782 | + if file_path: |
| 783 | + tar_cmd.append('-C') |
| 784 | + tar_cmd.append(file_path) |
| 785 | + tar_cmd.append(self.get_relative_filename(file)) |
| 786 | + else: |
| 787 | + tar_cmd.append(file) |
| 788 | + p1 = Popen(tar_cmd, stdout=PIPE) |
| 789 | + p2 = Popen(['gzip'], stdin=p1.stdout, stdout=outfile) |
| 790 | + p2.communicate() |
| 791 | + outfile.close |
| 792 | + if os.path.getsize(tar_file) <= 0: |
| 793 | + print 'Could not tar image' |
| 794 | + raise CommandFailed |
| 795 | + return tar_file |
| 796 | + |
| 797 | + def hexToBytes(self, hexString): |
| 798 | + bytes = [] |
| 799 | + hexString = ''.join(hexString.split(' ')) |
| 800 | + for i in range(0, len(hexString), 2): |
| 801 | + bytes.append(chr(int(hexString[i:i + 2], 16))) |
| 802 | + |
| 803 | + return ''.join(bytes) |
| 804 | + |
| 805 | + def crypt_file( |
| 806 | + self, |
| 807 | + cipher, |
| 808 | + in_file, |
| 809 | + out_file, |
| 810 | + ): |
| 811 | + while 1: |
| 812 | + buf = in_file.read(IMAGE_IO_CHUNK) |
| 813 | + if not buf: |
| 814 | + break |
| 815 | + out_file.write(cipher.update(buf)) |
| 816 | + out_file.write(cipher.final()) |
| 817 | + |
| 818 | + def encrypt_image(self, file): |
| 819 | + print 'Encrypting image' |
| 820 | + enc_file = '%s.part' % file.replace('.tar.gz', '') |
| 821 | + |
| 822 | + key = hex(BN.rand(16 * 8))[2:34].replace('L', 'c') |
| 823 | + if self.debug: |
| 824 | + print 'Key: %s' % key |
| 825 | + iv = hex(BN.rand(16 * 8))[2:34].replace('L', 'c') |
| 826 | + if self.debug: |
| 827 | + print 'IV: %s' % iv |
| 828 | + |
| 829 | + k = EVP.Cipher(alg='aes_128_cbc', key=unhexlify(key), |
| 830 | + iv=unhexlify(iv), op=1) |
| 831 | + |
| 832 | + in_file = open(file) |
| 833 | + out_file = open(enc_file, 'wb') |
| 834 | + self.crypt_file(k, in_file, out_file) |
| 835 | + in_file.close() |
| 836 | + out_file.close() |
| 837 | + bundled_size = os.path.getsize(enc_file) |
| 838 | + return (enc_file, key, iv, bundled_size) |
| 839 | + |
| 840 | + def split_image(self, file): |
| 841 | + print 'Splitting image...' |
| 842 | + return self.split_file(file, IMAGE_SPLIT_CHUNK) |
| 843 | + |
| 844 | + def get_verification_string(self, manifest_string): |
| 845 | + start_mc = manifest_string.find('<machine_configuration>') |
| 846 | + end_mc = manifest_string.find('</machine_configuration>') |
| 847 | + mc_config_string = manifest_string[start_mc:end_mc |
| 848 | + + len('</machine_configuration>')] |
| 849 | + start_image = manifest_string.find('<image>') |
| 850 | + end_image = manifest_string.find('</image>') |
| 851 | + image_string = manifest_string[start_image:end_image |
| 852 | + + len('</image>')] |
| 853 | + |
| 854 | + return mc_config_string + image_string |
| 855 | + |
| 856 | + def parse_manifest(self, manifest_filename): |
| 857 | + parts = [] |
| 858 | + encrypted_key = None |
| 859 | + encrypted_iv = None |
| 860 | + dom = minidom.parse(manifest_filename) |
| 861 | + manifest_elem = dom.getElementsByTagName('manifest')[0] |
| 862 | + parts_list = manifest_elem.getElementsByTagName('filename') |
| 863 | + for part_elem in parts_list: |
| 864 | + nodes = part_elem.childNodes |
| 865 | + for node in nodes: |
| 866 | + if node.nodeType == node.TEXT_NODE: |
| 867 | + parts.append(node.data) |
| 868 | + encrypted_key_elem = \ |
| 869 | + manifest_elem.getElementsByTagName('user_encrypted_key')[0] |
| 870 | + nodes = encrypted_key_elem.childNodes |
| 871 | + for node in nodes: |
| 872 | + if node.nodeType == node.TEXT_NODE: |
| 873 | + encrypted_key = node.data |
| 874 | + encrypted_iv_elem = \ |
| 875 | + manifest_elem.getElementsByTagName('user_encrypted_iv')[0] |
| 876 | + nodes = encrypted_iv_elem.childNodes |
| 877 | + for node in nodes: |
| 878 | + if node.nodeType == node.TEXT_NODE: |
| 879 | + encrypted_iv = node.data |
| 880 | + return (parts, encrypted_key, encrypted_iv) |
| 881 | + |
| 882 | + def assemble_parts( |
| 883 | + self, |
| 884 | + src_directory, |
| 885 | + directory, |
| 886 | + manifest_path, |
| 887 | + parts, |
| 888 | + ): |
| 889 | + manifest_filename = self.get_relative_filename(manifest_path) |
| 890 | + encrypted_filename = os.path.join(directory, |
| 891 | + manifest_filename.replace('.manifest.xml', '.enc.tar.gz' |
| 892 | + )) |
| 893 | + if len(parts) > 0: |
| 894 | + if not os.path.exists(directory): |
| 895 | + os.makedirs(directory) |
| 896 | + encrypted_file = open(encrypted_filename, 'wb') |
| 897 | + for part in parts: |
| 898 | + print 'Part:', self.get_relative_filename(part) |
| 899 | + part_filename = os.path.join(src_directory, part) |
| 900 | + part_file = open(part_filename, 'rb') |
| 901 | + while 1: |
| 902 | + data = part_file.read(IMAGE_IO_CHUNK) |
| 903 | + if not data: |
| 904 | + break |
| 905 | + encrypted_file.write(data) |
| 906 | + part_file.close() |
| 907 | + encrypted_file.close() |
| 908 | + return encrypted_filename |
| 909 | + |
| 910 | + def decrypt_image( |
| 911 | + self, |
| 912 | + encrypted_filename, |
| 913 | + encrypted_key, |
| 914 | + encrypted_iv, |
| 915 | + private_key_path, |
| 916 | + ): |
| 917 | + user_priv_key = RSA.load_key(private_key_path) |
| 918 | + key = user_priv_key.private_decrypt(unhexlify(encrypted_key), |
| 919 | + RSA.pkcs1_padding) |
| 920 | + iv = user_priv_key.private_decrypt(unhexlify(encrypted_iv), |
| 921 | + RSA.pkcs1_padding) |
| 922 | + k = EVP.Cipher(alg='aes_128_cbc', key=unhexlify(key), |
| 923 | + iv=unhexlify(iv), op=0) |
| 924 | + |
| 925 | + decrypted_filename = encrypted_filename.replace('.enc', '') |
| 926 | + decrypted_file = open(decrypted_filename, 'wb') |
| 927 | + encrypted_file = open(encrypted_filename, 'rb') |
| 928 | + self.crypt_file(k, encrypted_file, decrypted_file) |
| 929 | + encrypted_file.close() |
| 930 | + decrypted_file.close() |
| 931 | + return decrypted_filename |
| 932 | + |
| 933 | + def decrypt_string( |
| 934 | + self, |
| 935 | + encrypted_string, |
| 936 | + private_key_path, |
| 937 | + encoded=False, |
| 938 | + ): |
| 939 | + user_priv_key = RSA.load_key(private_key_path) |
| 940 | + string_to_decrypt = encrypted_string |
| 941 | + if encoded: |
| 942 | + string_to_decrypt = base64.b64decode(encrypted_string) |
| 943 | + return user_priv_key.private_decrypt(string_to_decrypt, |
| 944 | + RSA.pkcs1_padding) |
| 945 | + |
| 946 | + def untarzip_image(self, path, file): |
| 947 | + untarred_filename = file.replace('.tar.gz', '') |
| 948 | + tar_file = tarfile.open(file, 'r|gz') |
| 949 | + tar_file.extractall(path) |
| 950 | + untarred_names = tar_file.getnames() |
| 951 | + tar_file.close() |
| 952 | + return untarred_names |
| 953 | + |
| 954 | + def get_block_devs(self, mapping): |
| 955 | + virtual = [] |
| 956 | + devices = [] |
| 957 | + |
| 958 | + vname = None |
| 959 | + for m in mapping: |
| 960 | + if not vname: |
| 961 | + vname = m |
| 962 | + virtual.append(vname) |
| 963 | + else: |
| 964 | + devices.append(m) |
| 965 | + vname = None |
| 966 | + |
| 967 | + return (virtual, devices) |
| 968 | + |
| 969 | + def generate_manifest( |
| 970 | + self, |
| 971 | + path, |
| 972 | + prefix, |
| 973 | + parts, |
| 974 | + parts_digest, |
| 975 | + file, |
| 976 | + key, |
| 977 | + iv, |
| 978 | + cert_path, |
| 979 | + ec2cert_path, |
| 980 | + private_key_path, |
| 981 | + target_arch, |
| 982 | + image_size, |
| 983 | + bundled_size, |
| 984 | + image_digest, |
| 985 | + user, |
| 986 | + kernel, |
| 987 | + ramdisk, |
| 988 | + mapping=None, |
| 989 | + product_codes=None, |
| 990 | + ancestor_ami_ids=None, |
| 991 | + ): |
| 992 | + user_pub_key = X509.load_cert(cert_path).get_pubkey().get_rsa() |
| 993 | + cloud_pub_key = \ |
| 994 | + X509.load_cert(ec2cert_path).get_pubkey().get_rsa() |
| 995 | + |
| 996 | + user_encrypted_key = hexlify(user_pub_key.public_encrypt(key, |
| 997 | + RSA.pkcs1_padding)) |
| 998 | + user_encrypted_iv = hexlify(user_pub_key.public_encrypt(iv, |
| 999 | + RSA.pkcs1_padding)) |
| 1000 | + |
| 1001 | + cloud_encrypted_key = hexlify(cloud_pub_key.public_encrypt(key, |
| 1002 | + RSA.pkcs1_padding)) |
| 1003 | + cloud_encrypted_iv = hexlify(cloud_pub_key.public_encrypt(iv, |
| 1004 | + RSA.pkcs1_padding)) |
| 1005 | + |
| 1006 | + user_priv_key = RSA.load_key(private_key_path) |
| 1007 | + |
| 1008 | + manifest_file = '%s.manifest.xml' % os.path.join(path, prefix) |
| 1009 | + if self.debug: |
| 1010 | + print 'Manifest: ', manifest_file |
| 1011 | + |
| 1012 | + print 'Generating manifest %s' % manifest_file |
| 1013 | + |
| 1014 | + manifest_out_file = open(manifest_file, 'wb') |
| 1015 | + doc = Document() |
| 1016 | + |
| 1017 | + manifest_elem = doc.createElement('manifest') |
| 1018 | + doc.appendChild(manifest_elem) |
| 1019 | + |
| 1020 | + # version |
| 1021 | + |
| 1022 | + version_elem = doc.createElement('version') |
| 1023 | + version_value = doc.createTextNode(VERSION) |
| 1024 | + version_elem.appendChild(version_value) |
| 1025 | + manifest_elem.appendChild(version_elem) |
| 1026 | + |
| 1027 | + # bundler info |
| 1028 | + |
| 1029 | + bundler_elem = doc.createElement('bundler') |
| 1030 | + bundler_name_elem = doc.createElement('name') |
| 1031 | + bundler_name_value = doc.createTextNode(BUNDLER_NAME) |
| 1032 | + bundler_name_elem.appendChild(bundler_name_value) |
| 1033 | + bundler_version_elem = doc.createElement('version') |
| 1034 | + bundler_version_value = doc.createTextNode(BUNDLER_VERSION) |
| 1035 | + bundler_version_elem.appendChild(bundler_version_value) |
| 1036 | + bundler_elem.appendChild(bundler_name_elem) |
| 1037 | + bundler_elem.appendChild(bundler_version_elem) |
| 1038 | + |
| 1039 | + # release |
| 1040 | + |
| 1041 | + release_elem = doc.createElement('release') |
| 1042 | + release_value = doc.createTextNode(RELEASE) |
| 1043 | + release_elem.appendChild(release_value) |
| 1044 | + bundler_elem.appendChild(release_elem) |
| 1045 | + manifest_elem.appendChild(bundler_elem) |
| 1046 | + |
| 1047 | + # machine config |
| 1048 | + |
| 1049 | + machine_config_elem = doc.createElement('machine_configuration') |
| 1050 | + manifest_elem.appendChild(machine_config_elem) |
| 1051 | + |
| 1052 | + target_arch_elem = doc.createElement('architecture') |
| 1053 | + target_arch_value = doc.createTextNode(target_arch) |
| 1054 | + target_arch_elem.appendChild(target_arch_value) |
| 1055 | + machine_config_elem.appendChild(target_arch_elem) |
| 1056 | + |
| 1057 | + # block device mapping |
| 1058 | + |
| 1059 | + if mapping: |
| 1060 | + block_dev_mapping_elem = \ |
| 1061 | + doc.createElement('block_device_mapping') |
| 1062 | + (virtual_names, device_names) = self.get_block_devs(mapping) |
| 1063 | + vname_index = 0 |
| 1064 | + for vname in virtual_names: |
| 1065 | + dname = device_names[vname_index] |
| 1066 | + mapping_elem = doc.createElement('mapping') |
| 1067 | + virtual_elem = doc.createElement('virtual') |
| 1068 | + virtual_value = doc.createTextNode(vname) |
| 1069 | + virtual_elem.appendChild(virtual_value) |
| 1070 | + mapping_elem.appendChild(virtual_elem) |
| 1071 | + device_elem = doc.createElement('device') |
| 1072 | + device_value = doc.createTextNode(dname) |
| 1073 | + device_elem.appendChild(device_value) |
| 1074 | + mapping_elem.appendChild(device_elem) |
| 1075 | + block_dev_mapping_elem.appendChild(mapping_elem) |
| 1076 | + vname_index = vname_index + 1 |
| 1077 | + machine_config_elem.appendChild(block_dev_mapping_elem) |
| 1078 | + |
| 1079 | + if product_codes: |
| 1080 | + product_codes_elem = doc.createElement('product_codes') |
| 1081 | + for product_code in product_codes: |
| 1082 | + product_code_elem = doc.createElement('product_code') |
| 1083 | + product_code_value = doc.createTextNode(product_code) |
| 1084 | + product_code_elem.appendChild(product_code_value) |
| 1085 | + product_codes_elem.appendChild(product_code_elem) |
| 1086 | + machine_config_elem.appendChild(product_codes_elem) |
| 1087 | + |
| 1088 | + # kernel and ramdisk |
| 1089 | + |
| 1090 | + if kernel: |
| 1091 | + kernel_id_elem = doc.createElement('kernel_id') |
| 1092 | + kernel_id_value = doc.createTextNode(kernel) |
| 1093 | + kernel_id_elem.appendChild(kernel_id_value) |
| 1094 | + machine_config_elem.appendChild(kernel_id_elem) |
| 1095 | + |
| 1096 | + if ramdisk: |
| 1097 | + ramdisk_id_elem = doc.createElement('ramdisk_id') |
| 1098 | + ramdisk_id_value = doc.createTextNode(ramdisk) |
| 1099 | + ramdisk_id_elem.appendChild(ramdisk_id_value) |
| 1100 | + machine_config_elem.appendChild(ramdisk_id_elem) |
| 1101 | + |
| 1102 | + image_elem = doc.createElement('image') |
| 1103 | + manifest_elem.appendChild(image_elem) |
| 1104 | + |
| 1105 | + # name |
| 1106 | + |
| 1107 | + image_name_elem = doc.createElement('name') |
| 1108 | + image_name_value = \ |
| 1109 | + doc.createTextNode(self.get_relative_filename(file)) |
| 1110 | + image_name_elem.appendChild(image_name_value) |
| 1111 | + image_elem.appendChild(image_name_elem) |
| 1112 | + |
| 1113 | + # user |
| 1114 | + |
| 1115 | + user_elem = doc.createElement('user') |
| 1116 | + user_value = doc.createTextNode('%s' % user) |
| 1117 | + user_elem.appendChild(user_value) |
| 1118 | + image_elem.appendChild(user_elem) |
| 1119 | + |
| 1120 | + # type |
| 1121 | + # TODO: fixme |
| 1122 | + |
| 1123 | + image_type_elem = doc.createElement('type') |
| 1124 | + image_type_value = doc.createTextNode('machine') |
| 1125 | + image_type_elem.appendChild(image_type_value) |
| 1126 | + image_elem.appendChild(image_type_elem) |
| 1127 | + |
| 1128 | + # ancestor ami ids |
| 1129 | + |
| 1130 | + if ancestor_ami_ids: |
| 1131 | + ancestry_elem = doc.createElement('ancestry') |
| 1132 | + for ancestor_ami_id in ancestor_ami_ids: |
| 1133 | + ancestor_id_elem = doc.createElement('ancestor_ami_id') |
| 1134 | + ancestor_id_value = doc.createTextNode(ancestor_ami_id) |
| 1135 | + ancestor_id_elem.appendChild(ancestor_id_value) |
| 1136 | + ancestry_elem.appendChild(ancestor_id_elem) |
| 1137 | + image_elem.appendChild(ancestry_elem) |
| 1138 | + |
| 1139 | + # digest |
| 1140 | + |
| 1141 | + image_digest_elem = doc.createElement('digest') |
| 1142 | + image_digest_elem.setAttribute('algorithm', 'SHA1') |
| 1143 | + image_digest_value = doc.createTextNode('%s' % image_digest) |
| 1144 | + image_digest_elem.appendChild(image_digest_value) |
| 1145 | + image_elem.appendChild(image_digest_elem) |
| 1146 | + |
| 1147 | + # size |
| 1148 | + |
| 1149 | + image_size_elem = doc.createElement('size') |
| 1150 | + image_size_value = doc.createTextNode('%s' % image_size) |
| 1151 | + image_size_elem.appendChild(image_size_value) |
| 1152 | + image_elem.appendChild(image_size_elem) |
| 1153 | + |
| 1154 | + # bundled size |
| 1155 | + |
| 1156 | + bundled_size_elem = doc.createElement('bundled_size') |
| 1157 | + bundled_size_value = doc.createTextNode('%s' % bundled_size) |
| 1158 | + bundled_size_elem.appendChild(bundled_size_value) |
| 1159 | + image_elem.appendChild(bundled_size_elem) |
| 1160 | + |
| 1161 | + # key, iv |
| 1162 | + |
| 1163 | + cloud_encrypted_key_elem = doc.createElement('ec2_encrypted_key' |
| 1164 | + ) |
| 1165 | + cloud_encrypted_key_value = doc.createTextNode('%s' |
| 1166 | + % cloud_encrypted_key) |
| 1167 | + cloud_encrypted_key_elem.appendChild(cloud_encrypted_key_value) |
| 1168 | + cloud_encrypted_key_elem.setAttribute('algorithm', AES) |
| 1169 | + image_elem.appendChild(cloud_encrypted_key_elem) |
| 1170 | + |
| 1171 | + user_encrypted_key_elem = doc.createElement('user_encrypted_key' |
| 1172 | + ) |
| 1173 | + user_encrypted_key_value = doc.createTextNode('%s' |
| 1174 | + % user_encrypted_key) |
| 1175 | + user_encrypted_key_elem.appendChild(user_encrypted_key_value) |
| 1176 | + user_encrypted_key_elem.setAttribute('algorithm', AES) |
| 1177 | + image_elem.appendChild(user_encrypted_key_elem) |
| 1178 | + |
| 1179 | + cloud_encrypted_iv_elem = doc.createElement('ec2_encrypted_iv') |
| 1180 | + cloud_encrypted_iv_value = doc.createTextNode('%s' |
| 1181 | + % cloud_encrypted_iv) |
| 1182 | + cloud_encrypted_iv_elem.appendChild(cloud_encrypted_iv_value) |
| 1183 | + image_elem.appendChild(cloud_encrypted_iv_elem) |
| 1184 | + |
| 1185 | + user_encrypted_iv_elem = doc.createElement('user_encrypted_iv') |
| 1186 | + user_encrypted_iv_value = doc.createTextNode('%s' |
| 1187 | + % user_encrypted_iv) |
| 1188 | + user_encrypted_iv_elem.appendChild(user_encrypted_iv_value) |
| 1189 | + image_elem.appendChild(user_encrypted_iv_elem) |
| 1190 | + |
| 1191 | + # parts |
| 1192 | + |
| 1193 | + parts_elem = doc.createElement('parts') |
| 1194 | + parts_elem.setAttribute('count', '%s' % len(parts)) |
| 1195 | + part_number = 0 |
| 1196 | + for part in parts: |
| 1197 | + part_elem = doc.createElement('part') |
| 1198 | + filename_elem = doc.createElement('filename') |
| 1199 | + filename_value = \ |
| 1200 | + doc.createTextNode(self.get_relative_filename(part)) |
| 1201 | + filename_elem.appendChild(filename_value) |
| 1202 | + part_elem.appendChild(filename_elem) |
| 1203 | + |
| 1204 | + # digest |
| 1205 | + |
| 1206 | + part_digest_elem = doc.createElement('digest') |
| 1207 | + part_digest_elem.setAttribute('algorithm', 'SHA1') |
| 1208 | + part_digest_value = \ |
| 1209 | + doc.createTextNode(parts_digest[part_number]) |
| 1210 | + part_digest_elem.appendChild(part_digest_value) |
| 1211 | + part_elem.appendChild(part_digest_elem) |
| 1212 | + part_elem.setAttribute('index', '%s' % part_number) |
| 1213 | + parts_elem.appendChild(part_elem) |
| 1214 | + part_number += 1 |
| 1215 | + image_elem.appendChild(parts_elem) |
| 1216 | + |
| 1217 | + manifest_string = doc.toxml() |
| 1218 | + |
| 1219 | + string_to_sign = self.get_verification_string(manifest_string) |
| 1220 | + signature_elem = doc.createElement('signature') |
| 1221 | + sha_manifest = sha() |
| 1222 | + sha_manifest.update(string_to_sign) |
| 1223 | + signature_value = doc.createTextNode('%s' |
| 1224 | + % hexlify(user_priv_key.sign(sha_manifest.digest()))) |
| 1225 | + signature_elem.appendChild(signature_value) |
| 1226 | + manifest_elem.appendChild(signature_elem) |
| 1227 | + manifest_out_file.write(doc.toxml()) |
| 1228 | + manifest_out_file.close() |
| 1229 | + |
| 1230 | + def add_excludes(self, path, excludes): |
| 1231 | + if self.debug: |
| 1232 | + print 'Reading /etc/mtab...' |
| 1233 | + mtab_file = open('/etc/mtab', 'r') |
| 1234 | + while 1: |
| 1235 | + mtab_line = mtab_file.readline() |
| 1236 | + if not mtab_line: |
| 1237 | + break |
| 1238 | + mtab_line_parts = mtab_line.split(' ') |
| 1239 | + mount_point = mtab_line_parts[1] |
| 1240 | + fs_type = mtab_line_parts[2] |
| 1241 | + if mount_point.find(path) == 0 and fs_type \ |
| 1242 | + not in self.img.ALLOWED_FS_TYPES: |
| 1243 | + if self.debug: |
| 1244 | + print 'Excluding %s...' % mount_point |
| 1245 | + excludes.append(mount_point) |
| 1246 | + mtab_file.close() |
| 1247 | + for banned in self.img.BANNED_MOUNTS: |
| 1248 | + excludes.append(banned) |
| 1249 | + |
| 1250 | + def make_image( |
| 1251 | + self, |
| 1252 | + size_in_MB, |
| 1253 | + excludes, |
| 1254 | + prefix, |
| 1255 | + destination_path, |
| 1256 | + ): |
| 1257 | + image_file = '%s.img' % prefix |
| 1258 | + image_path = '%s/%s' % (destination_path, image_file) |
| 1259 | + if not os.path.exists(destination_path): |
| 1260 | + os.makedirs(destination_path) |
| 1261 | + if self.img == 'Unsupported': |
| 1262 | + print 'Platform not fully supported.' |
| 1263 | + raise UnsupportedException |
| 1264 | + self.img.create_image(size_in_MB, image_path) |
| 1265 | + self.img.make_fs(image_path) |
| 1266 | + return image_path |
| 1267 | + |
| 1268 | + def create_loopback(self, image_path): |
| 1269 | + Util().check_prerequisite_command('losetup') |
| 1270 | + tries = 0 |
| 1271 | + while tries < MAX_LOOP_DEVS: |
| 1272 | + loop_dev = Popen(['losetup', '-f'], |
| 1273 | + stdout=PIPE).communicate()[0].replace('\n' |
| 1274 | + , '') |
| 1275 | + if loop_dev: |
| 1276 | + output = Popen(['losetup', '%s' % loop_dev, '%s' |
| 1277 | + % image_path], stdout=PIPE, |
| 1278 | + stderr=PIPE).communicate() |
| 1279 | + if not output[1]: |
| 1280 | + return loop_dev |
| 1281 | + else: |
| 1282 | + print 'Could not create loopback device. Aborting' |
| 1283 | + raise CommandFailed |
| 1284 | + tries += 1 |
| 1285 | + |
| 1286 | + def mount_image(self, image_path): |
| 1287 | + Util().check_prerequisite_command('mount') |
| 1288 | + |
| 1289 | + tmp_mnt_point = '/tmp/%s' % hex(BN.rand(16))[2:6] |
| 1290 | + if not os.path.exists(tmp_mnt_point): |
| 1291 | + os.makedirs(tmp_mnt_point) |
| 1292 | + if self.debug: |
| 1293 | + print 'Creating loopback device...' |
| 1294 | + loop_dev = self.create_loopback(image_path) |
| 1295 | + if self.debug: |
| 1296 | + print 'Mounting image...' |
| 1297 | + Popen(['mount', loop_dev, tmp_mnt_point], |
| 1298 | + stdout=PIPE).communicate() |
| 1299 | + return (tmp_mnt_point, loop_dev) |
| 1300 | + |
| 1301 | + def copy_to_image( |
| 1302 | + self, |
| 1303 | + mount_point, |
| 1304 | + volume_path, |
| 1305 | + excludes, |
| 1306 | + ): |
| 1307 | + try: |
| 1308 | + Util().check_prerequisite_command('rsync') |
| 1309 | + except NotFoundError: |
| 1310 | + raise CopyError |
| 1311 | + rsync_cmd = ['rsync', '-aXS'] |
| 1312 | + for exclude in excludes: |
| 1313 | + rsync_cmd.append('--exclude') |
| 1314 | + rsync_cmd.append(exclude) |
| 1315 | + rsync_cmd.append(volume_path) |
| 1316 | + rsync_cmd.append(mount_point) |
| 1317 | + if self.debug: |
| 1318 | + print 'Copying files...' |
| 1319 | + for exclude in excludes: |
| 1320 | + print 'Excluding:', exclude |
| 1321 | + |
| 1322 | + pipe = Popen(rsync_cmd, stdout=PIPE, stderr=PIPE) |
| 1323 | + output = pipe.communicate() |
| 1324 | + for dir in self.img.ESSENTIAL_DIRS: |
| 1325 | + dir_path = os.path.join(mount_point, dir) |
| 1326 | + if not os.path.exists(dir_path): |
| 1327 | + os.mkdir(dir_path) |
| 1328 | + if dir == 'tmp': |
| 1329 | + os.chmod(dir_path, 01777) |
| 1330 | + self.img.make_essential_devs(mount_point) |
| 1331 | + mtab_file = open('/etc/mtab', 'r') |
| 1332 | + while 1: |
| 1333 | + mtab_line = mtab_file.readline() |
| 1334 | + if not mtab_line: |
| 1335 | + break |
| 1336 | + mtab_line_parts = mtab_line.split(' ') |
| 1337 | + mount_location = mtab_line_parts[1] |
| 1338 | + fs_type = mtab_line_parts[2] |
| 1339 | + if fs_type == 'tmpfs': |
| 1340 | + mount_location = mount_location[1:] |
| 1341 | + dir_path = os.path.join(mount_point, mount_location) |
| 1342 | + if not os.path.exists(dir_path): |
| 1343 | + if self.debug: |
| 1344 | + print 'Making essential directory %s' \ |
| 1345 | + % mount_location |
| 1346 | + os.makedirs(dir_path) |
| 1347 | + mtab_file.close() |
| 1348 | + if pipe.returncode: |
| 1349 | + |
| 1350 | + # rsync return code 23: Partial transfer due to error |
| 1351 | + # rsync return code 24: Partial transfer due to vanished source files |
| 1352 | + |
| 1353 | + if pipe.returncode in (23, 24): |
| 1354 | + print 'Warning: rsync reports files partially copied:' |
| 1355 | + print output |
| 1356 | + else: |
| 1357 | + print 'Error: rsync failed with return code %d' \ |
| 1358 | + % pipe.returncode |
| 1359 | + raise CopyError |
| 1360 | + |
| 1361 | + def unmount_image(self, mount_point): |
| 1362 | + Util().check_prerequisite_command('umount') |
| 1363 | + if self.debug: |
| 1364 | + print 'Unmounting image...' |
| 1365 | + Popen(['umount', '-d', mount_point], |
| 1366 | + stdout=PIPE).communicate()[0] |
| 1367 | + os.rmdir(mount_point) |
| 1368 | + |
| 1369 | + def copy_volume( |
| 1370 | + self, |
| 1371 | + image_path, |
| 1372 | + volume_path, |
| 1373 | + excludes, |
| 1374 | + generate_fstab, |
| 1375 | + fstab_path, |
| 1376 | + ): |
| 1377 | + (mount_point, loop_dev) = self.mount_image(image_path) |
| 1378 | + try: |
| 1379 | + output = self.copy_to_image(mount_point, volume_path, |
| 1380 | + excludes) |
| 1381 | + if self.img == 'Unsupported': |
| 1382 | + print 'Platform not fully supported.' |
| 1383 | + raise UnsupportedException |
| 1384 | + self.img.add_fstab(mount_point, generate_fstab, fstab_path) |
| 1385 | + except CopyError: |
| 1386 | + raise CopyError |
| 1387 | + finally: |
| 1388 | + self.unmount_image(mount_point) |
| 1389 | + |
| 1390 | + def can_read_instance_metadata(self): |
| 1391 | + meta_data = urllib.urlopen(METADATA_URL) |
| 1392 | + |
| 1393 | + def get_instance_metadata(self, type): |
| 1394 | + if self.debug: |
| 1395 | + print 'Reading instance metadata', type |
| 1396 | + metadata = urllib.urlopen(METADATA_URL + type).read() |
| 1397 | + if 'Not' in metadata and 'Found' in metadata and '404' \ |
| 1398 | + in metadata: |
| 1399 | + raise MetadataReadError |
| 1400 | + return metadata |
| 1401 | + |
| 1402 | + def get_instance_ramdisk(self): |
| 1403 | + return self.get_instance_metadata('ramdisk-id') |
| 1404 | + |
| 1405 | + def get_instance_kernel(self): |
| 1406 | + return self.get_instance_metadata('kernel-id') |
| 1407 | + |
| 1408 | + def get_instance_product_codes(self): |
| 1409 | + return self.get_instance_metadata('product-codes') |
| 1410 | + |
| 1411 | + def get_ancestor_ami_ids(self): |
| 1412 | + return self.get_instance_metadata('ancestor-ami-ids') |
| 1413 | + |
| 1414 | + def get_instance_block_device_mappings(self): |
| 1415 | + keys = self.get_instance_metadata('block-device-mapping' |
| 1416 | + ).split('\n') |
| 1417 | + mapping = [] |
| 1418 | + for k in keys: |
| 1419 | + mapping.append(k) |
| 1420 | + mapping.append(self.get_instance_metadata(os.path.join('block-device-mapping' |
| 1421 | + , k))) |
| 1422 | + return mapping |
| 1423 | + |
| 1424 | + def display_error_and_exit(self, msg): |
| 1425 | + code = None |
| 1426 | + message = None |
| 1427 | + index = msg.find('<') |
| 1428 | + if index < 0: |
| 1429 | + print msg |
| 1430 | + sys.exit(1) |
| 1431 | + msg = msg[index - 1:] |
| 1432 | + msg = msg.replace('\n', '') |
| 1433 | + dom = minidom.parseString(msg) |
| 1434 | + try: |
| 1435 | + error_elem = dom.getElementsByTagName('Error')[0] |
| 1436 | + code_elem = error_elem.getElementsByTagName('Code')[0] |
| 1437 | + nodes = code_elem.childNodes |
| 1438 | + for node in nodes: |
| 1439 | + if node.nodeType == node.TEXT_NODE: |
| 1440 | + code = node.data |
| 1441 | + |
| 1442 | + msg_elem = error_elem.getElementsByTagName('Message')[0] |
| 1443 | + nodes = msg_elem.childNodes |
| 1444 | + for node in nodes: |
| 1445 | + if node.nodeType == node.TEXT_NODE: |
| 1446 | + message = node.data |
| 1447 | + |
| 1448 | + print '%s:' % code, message |
| 1449 | + except Exception: |
| 1450 | + print msg |
| 1451 | + sys.exit(1) |
| 1452 | + |
| 1453 | + def parse_block_device_args(self, block_device_maps_args): |
| 1454 | + block_device_map = BlockDeviceMapping() |
| 1455 | + for block_device_map_arg in block_device_maps_args: |
| 1456 | + parts = block_device_map_arg.split('=') |
| 1457 | + if len(parts) > 1: |
| 1458 | + device_name = parts[0] |
| 1459 | + block_dev_type = EBSBlockDeviceType() |
| 1460 | + value_parts = parts[1].split(':') |
| 1461 | + if value_parts[0].startswith('snap'): |
| 1462 | + block_dev_type.snapshot_id = value_parts[0] |
| 1463 | + else: |
| 1464 | + if value_parts[0].startswith('ephemeral'): |
| 1465 | + block_dev_type.ephemeral_name = value_parts[0] |
| 1466 | + if len(value_parts) > 1: |
| 1467 | + block_dev_type.size = int(value_parts[1]) |
| 1468 | + if len(value_parts) > 2: |
| 1469 | + if value_parts[2] == 'true': |
| 1470 | + block_dev_type.delete_on_termination = True |
| 1471 | + block_device_map[device_name] = block_dev_type |
| 1472 | + return block_device_map |
| 1473 | + |
| 1474 | + |
| 1475 | +# read the config file 'config', update 'dict', setting |
| 1476 | +# the value from the config file for each element in array 'keylist' |
| 1477 | +# "config" is a bash syntax file defining bash variables |
| 1478 | + |
| 1479 | + |
| 1480 | +def parse_config(config, dict, keylist): |
| 1481 | + fmt = '' |
| 1482 | + str = '' |
| 1483 | + for v in keylist: |
| 1484 | + str = '%s "${%s}" ' % (str, v) |
| 1485 | + fmt = fmt + '%s%s' % ('%s', '\\0') |
| 1486 | + |
| 1487 | + cmd = ['bash', '-ec', ". '%s' >/dev/null; printf '%s' %s" |
| 1488 | + % (config, fmt, str)] |
| 1489 | + |
| 1490 | + handle = Popen(cmd, stderr=PIPE, stdout=PIPE) |
| 1491 | + (stdout, stderr) = handle.communicate() |
| 1492 | + if handle.returncode != 0: |
| 1493 | + raise ParseError('Parsing config file %s failed:\n\t%s' |
| 1494 | + % (config, stderr)) |
| 1495 | + |
| 1496 | + values = stdout.split("\0") |
| 1497 | + for i in range(len(values) - 1): |
| 1498 | + if values[i] != '': |
| 1499 | + dict[keylist[i]] = values[i] |
| 1500 | + |
| 1501 | + |
| 1502 | |
| 1503 | === added directory '.pc/bundle-image-usage-on-invalid-user.patch' |
| 1504 | === added file '.pc/bundle-image-usage-on-invalid-user.patch/.timestamp' |
| 1505 | === added directory '.pc/bundle-image-usage-on-invalid-user.patch/bin' |
| 1506 | === added file '.pc/bundle-image-usage-on-invalid-user.patch/bin/euca-bundle-image' |
| 1507 | --- .pc/bundle-image-usage-on-invalid-user.patch/bin/euca-bundle-image 1970-01-01 00:00:00 +0000 |
| 1508 | +++ .pc/bundle-image-usage-on-invalid-user.patch/bin/euca-bundle-image 2010-11-17 22:00:42 +0000 |
| 1509 | @@ -0,0 +1,285 @@ |
| 1510 | +#!/usr/bin/python |
| 1511 | +# -*- coding: utf-8 -*- |
| 1512 | + |
| 1513 | +# Software License Agreement (BSD License) |
| 1514 | +# |
| 1515 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 1516 | +# All rights reserved. |
| 1517 | +# |
| 1518 | +# Redistribution and use of this software in source and binary forms, with or |
| 1519 | +# without modification, are permitted provided that the following conditions |
| 1520 | +# are met: |
| 1521 | +# |
| 1522 | +# Redistributions of source code must retain the above |
| 1523 | +# copyright notice, this list of conditions and the |
| 1524 | +# following disclaimer. |
| 1525 | +# |
| 1526 | +# Redistributions in binary form must reproduce the above |
| 1527 | +# copyright notice, this list of conditions and the |
| 1528 | +# following disclaimer in the documentation and/or other |
| 1529 | +# materials provided with the distribution. |
| 1530 | +# |
| 1531 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 1532 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 1533 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 1534 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 1535 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 1536 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 1537 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 1538 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 1539 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 1540 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 1541 | +# POSSIBILITY OF SUCH DAMAGE. |
| 1542 | +# |
| 1543 | +# Author: Neil Soman neil@eucalyptus.com |
| 1544 | + |
| 1545 | +import getopt |
| 1546 | +import sys |
| 1547 | +import os |
| 1548 | +from euca2ools import Euca2ool, FileValidationError, Util, \ |
| 1549 | + NotFoundError, CommandFailed |
| 1550 | + |
| 1551 | +usage_string = \ |
| 1552 | + """ |
| 1553 | +Bundles an image for use with Eucalyptus or Amazon EC2. |
| 1554 | + |
| 1555 | +euca-bundle-image -i, --image image_path -u, --user user [-c, --cert cert_path] |
| 1556 | +[-k, --privatekey private_key_path] [-p, --prefix prefix] [--kernel kernel_id] |
| 1557 | +[--ramdisk ramdisk_id] [-B, --block-device-mapping mapping] |
| 1558 | +[-d, --destination destination_path] [--ec2cert ec2cert_path] |
| 1559 | +[-r, --arch target_architecture] [--batch] [-h, --help] [--version] [--debug] |
| 1560 | + |
| 1561 | +REQUIRED PARAMETERS |
| 1562 | + |
| 1563 | +-i, --image Path to the image file to bundle. |
| 1564 | + |
| 1565 | +-u, --user User ID (12-digit) of the user who is bundling the image. |
| 1566 | + |
| 1567 | +OPTIONAL PARAMETERS |
| 1568 | + |
| 1569 | +-c, --cert Path to the user's PEM encoded certificate. |
| 1570 | + |
| 1571 | +-k, --privatekey Path to the user's PEM encoded private key. |
| 1572 | + |
| 1573 | +-p, --prefix The prefix for the bundle image files. (default: image name). |
| 1574 | + |
| 1575 | +--kernel The kernel to be associated with the bundled image. |
| 1576 | + |
| 1577 | +--ramdisk The ramdisk to be associated with the bundled image. |
| 1578 | + |
| 1579 | +-B, --block-device-mapping Default block device mapping for the image (comma-separated list of key=value pairs). |
| 1580 | + |
| 1581 | +-d, --destination Directory to store the bundled image in (default: "/tmp"). Recommended. |
| 1582 | + |
| 1583 | +--ec2cert The path to the Cloud's X509 public key certificate. |
| 1584 | + |
| 1585 | +-r, --arch Target architecture for the image ('x86_64' or 'i386' default: 'x86_64'). |
| 1586 | + |
| 1587 | +--batch Run in batch mode (compatibility only. has no effect). |
| 1588 | +""" |
| 1589 | + |
| 1590 | + |
| 1591 | +def usage(status=1): |
| 1592 | + print usage_string |
| 1593 | + Util().usage() |
| 1594 | + sys.exit(status) |
| 1595 | + |
| 1596 | + |
| 1597 | +def version(): |
| 1598 | + print Util().version() |
| 1599 | + sys.exit() |
| 1600 | + |
| 1601 | + |
| 1602 | +def get_block_devs(mapping_str): |
| 1603 | + mapping = [] |
| 1604 | + mapping_pairs = mapping_str.split(',') |
| 1605 | + for m in mapping_pairs: |
| 1606 | + m_parts = m.split('=') |
| 1607 | + if len(m_parts) > 1: |
| 1608 | + mapping.append(m_parts[0]) |
| 1609 | + mapping.append(m_parts[1]) |
| 1610 | + return mapping |
| 1611 | + |
| 1612 | + |
| 1613 | +def add_product_codes(product_code_string, product_codes): |
| 1614 | + if not product_codes: |
| 1615 | + product_codes = [] |
| 1616 | + product_code_values = product_code_string.split(',') |
| 1617 | + |
| 1618 | + for p in product_code_values: |
| 1619 | + product_codes.append(p) |
| 1620 | + |
| 1621 | + return product_codes |
| 1622 | + |
| 1623 | + |
| 1624 | +def main(): |
| 1625 | + euca = None |
| 1626 | + try: |
| 1627 | + euca = Euca2ool('i:c:k:u:B:d:br:p:', [ |
| 1628 | + 'image=', |
| 1629 | + 'cert=', |
| 1630 | + 'privatekey=', |
| 1631 | + 'user=', |
| 1632 | + 'prefix=', |
| 1633 | + 'kernel=', |
| 1634 | + 'ramdisk=', |
| 1635 | + 'block-device-mapping=', |
| 1636 | + 'destination=', |
| 1637 | + 'ec2cert=', |
| 1638 | + 'arch=', |
| 1639 | + 'productcodes=', |
| 1640 | + 'batch', |
| 1641 | + ]) |
| 1642 | + except Exception, e: |
| 1643 | + print e |
| 1644 | + usage() |
| 1645 | + |
| 1646 | + image_path = None |
| 1647 | + kernel = None |
| 1648 | + user = None |
| 1649 | + ramdisk = None |
| 1650 | + try: |
| 1651 | + cert_path = euca.get_environ('EC2_CERT') |
| 1652 | + private_key_path = euca.get_environ('EC2_PRIVATE_KEY') |
| 1653 | + ec2cert_path = euca.get_environ('EUCALYPTUS_CERT') |
| 1654 | + user_string = euca.get_environ('EC2_USER_ID') |
| 1655 | + except NotFoundError: |
| 1656 | + sys.exit(1) |
| 1657 | + |
| 1658 | + prefix = None |
| 1659 | + destination_path = '/tmp' |
| 1660 | + target_arch = 'x86_64' |
| 1661 | + mapping = None |
| 1662 | + product_codes = None |
| 1663 | + product_code_string = None |
| 1664 | + if user_string: |
| 1665 | + try: |
| 1666 | + user = int(user_string) |
| 1667 | + except ValueError: |
| 1668 | + print 'Invalid user', user_string |
| 1669 | + sys.exit() |
| 1670 | + user = user_string |
| 1671 | + |
| 1672 | + for (name, value) in euca.opts: |
| 1673 | + if name in ('-h', '--help'): |
| 1674 | + usage(0) |
| 1675 | + elif name in ('-i', '--image'): |
| 1676 | + image_path = value |
| 1677 | + elif name in ('-c', '--cert'): |
| 1678 | + cert_path = value |
| 1679 | + elif name in ('-k', '--privatekey'): |
| 1680 | + private_key_path = value |
| 1681 | + elif name in ('-u', '--user'): |
| 1682 | + try: |
| 1683 | + value = value.replace('-', '') |
| 1684 | + user = int(value) |
| 1685 | + except ValueError: |
| 1686 | + print 'Invalid user', value |
| 1687 | + sys.exit() |
| 1688 | + user = value |
| 1689 | + elif name == '--kernel': |
| 1690 | + kernel = value |
| 1691 | + elif name == '--ramdisk': |
| 1692 | + ramdisk = value |
| 1693 | + elif name in ('-p', '--prefix'): |
| 1694 | + prefix = value |
| 1695 | + elif name in ('-d', '--destination'): |
| 1696 | + destination_path = value |
| 1697 | + elif name == '--ec2cert': |
| 1698 | + ec2cert_path = value |
| 1699 | + elif name in ('-r', '--arch'): |
| 1700 | + target_arch = value |
| 1701 | + print target_arch |
| 1702 | + if target_arch != 'i386' and target_arch != 'x86_64': |
| 1703 | + print 'target architecture must be i386 or x86_64' |
| 1704 | + usage() |
| 1705 | + elif name in ('-B', '--block-device-mapping'): |
| 1706 | + mapping = value |
| 1707 | + elif name == '--productcodes': |
| 1708 | + product_code_string = value |
| 1709 | + elif name == '--version': |
| 1710 | + version() |
| 1711 | + |
| 1712 | + if image_path and cert_path and private_key_path and user \ |
| 1713 | + and ec2cert_path: |
| 1714 | + try: |
| 1715 | + euca.validate_file(image_path) |
| 1716 | + except FileValidationError: |
| 1717 | + print 'Invalid image' |
| 1718 | + sys.exit(1) |
| 1719 | + try: |
| 1720 | + euca.validate_file(cert_path) |
| 1721 | + except FileValidationError: |
| 1722 | + print 'Invalid cert' |
| 1723 | + sys.exit(1) |
| 1724 | + try: |
| 1725 | + euca.validate_file(private_key_path) |
| 1726 | + except FileValidationError: |
| 1727 | + print 'Invalid private key' |
| 1728 | + sys.exit(1) |
| 1729 | + try: |
| 1730 | + euca.validate_file(ec2cert_path) |
| 1731 | + except FileValidationError: |
| 1732 | + print 'Invalid ec2cert' |
| 1733 | + sys.exit(1) |
| 1734 | + |
| 1735 | + (image_size, sha_image_digest) = euca.check_image(image_path, |
| 1736 | + destination_path) |
| 1737 | + if not prefix: |
| 1738 | + prefix = euca.get_relative_filename(image_path) |
| 1739 | + try: |
| 1740 | + tgz_file = euca.tarzip_image(prefix, image_path, |
| 1741 | + destination_path) |
| 1742 | + except NotFoundError: |
| 1743 | + sys.exit(1) |
| 1744 | + except CommandFailed: |
| 1745 | + sys.exit(1) |
| 1746 | + |
| 1747 | + (encrypted_file, key, iv, bundled_size) = \ |
| 1748 | + euca.encrypt_image(tgz_file) |
| 1749 | + os.remove(tgz_file) |
| 1750 | + (parts, parts_digest) = euca.split_image(encrypted_file) |
| 1751 | + if mapping: |
| 1752 | + mapping = get_block_devs(mapping) |
| 1753 | + if product_code_string: |
| 1754 | + product_codes = add_product_codes(product_code_string, |
| 1755 | + product_codes) |
| 1756 | + euca.generate_manifest( |
| 1757 | + destination_path, |
| 1758 | + prefix, |
| 1759 | + parts, |
| 1760 | + parts_digest, |
| 1761 | + image_path, |
| 1762 | + key, |
| 1763 | + iv, |
| 1764 | + cert_path, |
| 1765 | + ec2cert_path, |
| 1766 | + private_key_path, |
| 1767 | + target_arch, |
| 1768 | + image_size, |
| 1769 | + bundled_size, |
| 1770 | + sha_image_digest, |
| 1771 | + user, |
| 1772 | + kernel, |
| 1773 | + ramdisk, |
| 1774 | + mapping, |
| 1775 | + product_codes, |
| 1776 | + ) |
| 1777 | + os.remove(encrypted_file) |
| 1778 | + else: |
| 1779 | + if not image_path: |
| 1780 | + print 'image be specified.' |
| 1781 | + if not cert_path: |
| 1782 | + print 'cert must be specified.' |
| 1783 | + if not private_key_path: |
| 1784 | + print 'private key must be specified.' |
| 1785 | + if not user: |
| 1786 | + print 'user must be specified.' |
| 1787 | + if not ec2cert_path: |
| 1788 | + print 'ec2cert must be specified.' |
| 1789 | + usage() |
| 1790 | + |
| 1791 | + |
| 1792 | +if __name__ == '__main__': |
| 1793 | + main() |
| 1794 | + |
| 1795 | |
| 1796 | === added directory '.pc/bundle-vol-exclude-persistent-udev-net-rules.patch' |
| 1797 | === added file '.pc/bundle-vol-exclude-persistent-udev-net-rules.patch/.timestamp' |
| 1798 | === added directory '.pc/bundle-vol-exclude-persistent-udev-net-rules.patch/bin' |
| 1799 | === added file '.pc/bundle-vol-exclude-persistent-udev-net-rules.patch/bin/euca-bundle-vol' |
| 1800 | --- .pc/bundle-vol-exclude-persistent-udev-net-rules.patch/bin/euca-bundle-vol 1970-01-01 00:00:00 +0000 |
| 1801 | +++ .pc/bundle-vol-exclude-persistent-udev-net-rules.patch/bin/euca-bundle-vol 2010-11-17 22:00:42 +0000 |
| 1802 | @@ -0,0 +1,456 @@ |
| 1803 | +#!/usr/bin/python |
| 1804 | +# -*- coding: utf-8 -*- |
| 1805 | + |
| 1806 | +# Software License Agreement (BSD License) |
| 1807 | +# |
| 1808 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 1809 | +# All rights reserved. |
| 1810 | +# |
| 1811 | +# Redistribution and use of this software in source and binary forms, with or |
| 1812 | +# without modification, are permitted provided that the following conditions |
| 1813 | +# are met: |
| 1814 | +# |
| 1815 | +# Redistributions of source code must retain the above |
| 1816 | +# copyright notice, this list of conditions and the |
| 1817 | +# following disclaimer. |
| 1818 | +# |
| 1819 | +# Redistributions in binary form must reproduce the above |
| 1820 | +# copyright notice, this list of conditions and the |
| 1821 | +# following disclaimer in the documentation and/or other |
| 1822 | +# materials provided with the distribution. |
| 1823 | +# |
| 1824 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 1825 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 1826 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 1827 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 1828 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 1829 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 1830 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 1831 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 1832 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 1833 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 1834 | +# POSSIBILITY OF SUCH DAMAGE. |
| 1835 | +# |
| 1836 | +# Author: Neil Soman neil@eucalyptus.com |
| 1837 | + |
| 1838 | +import getopt |
| 1839 | +import sys |
| 1840 | +import os |
| 1841 | +from euca2ools import Euca2ool, FileValidationError, \ |
| 1842 | + DirValidationError, CopyError, MetadataReadError, Util, \ |
| 1843 | + NotFoundError, CommandFailed, UnsupportedException |
| 1844 | +from subprocess import * |
| 1845 | +import platform |
| 1846 | + |
| 1847 | +usage_string = \ |
| 1848 | + """ |
| 1849 | +Bundle the local filesystem of a running instance as a bundled image. |
| 1850 | + |
| 1851 | +euca-bundle-vol -u, --user user -s, --size size_in_MB |
| 1852 | +[-c, --cert cert_path] [-k, --privatekey private_key_path] |
| 1853 | +[-a, --all] [-e, --exclude dir1, dir2,...dirN] [-p, --prefix prefix] [--[no-]inherit] [-v, --volume volume_path] [--fstab fstab_path] [--generate-fstab] [--kernel kernel_id] [--ramdisk ramdisk_id] [-B, --block-device-mapping mapping] |
| 1854 | +[-d, --destination destination_path] [--ec2cert ec2cert_path] [-r, --arch target_architecture] [--batch] [--version] |
| 1855 | + |
| 1856 | +REQUIRED PARAMETERS |
| 1857 | + |
| 1858 | +-u, --user User ID (12-digit) of the user who is bundling the image. |
| 1859 | + |
| 1860 | +-s, --size Size for the image in MB (default: 10GB or 10240MB). |
| 1861 | + |
| 1862 | +OPTIONAL PARAMETERS |
| 1863 | + |
| 1864 | +-c, --cert Path to the user's PEM encoded certificate. |
| 1865 | + |
| 1866 | +-k, --privatekey Path to the user's PEM encoded private key. |
| 1867 | + |
| 1868 | +-a, --all Bundle all directories (including mounted filesystems). |
| 1869 | + |
| 1870 | +-p, --prefix The prefix for the bundle image files. (default: image name). |
| 1871 | + |
| 1872 | +--[no-]inherit Add (or do not add) instance metadata to the bundled image. Inherit is set by default. |
| 1873 | + |
| 1874 | +-e, --exclude comma-separated list of directories to exclude. |
| 1875 | + |
| 1876 | +--kernel The kernel to be associated with the bundled image. |
| 1877 | + |
| 1878 | +--ramdisk The ramdisk to be associated with the bundled image. |
| 1879 | + |
| 1880 | +-B, --block-device-mapping Default block device mapping for the image (comma-separated list of key=value pairs). |
| 1881 | + |
| 1882 | +-d, --destination Directory to store the bundled image in (default: "/tmp"). Recommended. |
| 1883 | + |
| 1884 | +--ec2cert The path to the Cloud's X509 public key certificate. |
| 1885 | + |
| 1886 | +-r, --arch Target architecture for the image ('x86_64' or 'i386' default: 'x86_64'). |
| 1887 | + |
| 1888 | +-v, --volume Path to mounted volume to create the bundle from (default: "/"). |
| 1889 | + |
| 1890 | +--fstab Path to the fstab to be bundled into the image. |
| 1891 | + |
| 1892 | +--generate-fstab Generate fstab to bundle into the image. |
| 1893 | + |
| 1894 | +--batch Run in batch mode (compatibility only. has no effect). |
| 1895 | +""" |
| 1896 | + |
| 1897 | +MAX_IMAGE_SIZE = 1024 * 10 |
| 1898 | + |
| 1899 | + |
| 1900 | +def usage(status=1): |
| 1901 | + print usage_string |
| 1902 | + sys.exit(status) |
| 1903 | + |
| 1904 | + |
| 1905 | +def version(): |
| 1906 | + print Util().version() |
| 1907 | + sys.exit() |
| 1908 | + |
| 1909 | + |
| 1910 | +def check_root(): |
| 1911 | + if os.geteuid() == 0: |
| 1912 | + return |
| 1913 | + else: |
| 1914 | + print 'Must be superuser to execute this command.' |
| 1915 | + sys.exit() |
| 1916 | + |
| 1917 | + |
| 1918 | +def check_image_size(size): |
| 1919 | + if size > MAX_IMAGE_SIZE: |
| 1920 | + print 'Image Size is too large (Max = %d MB)' % MAX_IMAGE_SIZE |
| 1921 | + sys.exit() |
| 1922 | + |
| 1923 | + |
| 1924 | +def parse_excludes(excludes_string): |
| 1925 | + excludes = [] |
| 1926 | + if excludes_string: |
| 1927 | + excludes_string = excludes_string.replace(' ', '') |
| 1928 | + excludes = excludes_string.split(',') |
| 1929 | + return excludes |
| 1930 | + |
| 1931 | + |
| 1932 | +def get_instance_metadata( |
| 1933 | + euca, |
| 1934 | + ramdisk, |
| 1935 | + kernel, |
| 1936 | + mapping, |
| 1937 | + ): |
| 1938 | + product_codes = None |
| 1939 | + ramdisk_id = ramdisk |
| 1940 | + kernel_id = kernel |
| 1941 | + block_dev_mapping = mapping |
| 1942 | + ancestor_ami_ids = None |
| 1943 | + try: |
| 1944 | + euca.can_read_instance_metadata() |
| 1945 | + if not ramdisk_id: |
| 1946 | + try: |
| 1947 | + ramdisk_id = euca.get_instance_ramdisk() |
| 1948 | + except MetadataReadError: |
| 1949 | + print 'Unable to read ramdisk id' |
| 1950 | + |
| 1951 | + if not kernel_id: |
| 1952 | + try: |
| 1953 | + kernel_id = euca.get_instance_kernel() |
| 1954 | + except MetadataReadError: |
| 1955 | + print 'Unable to read kernel id' |
| 1956 | + |
| 1957 | + if not block_dev_mapping: |
| 1958 | + try: |
| 1959 | + block_dev_mapping = \ |
| 1960 | + euca.get_instance_block_device_mappings() |
| 1961 | + except MetadataReadError: |
| 1962 | + print 'Unable to read block device mapping' |
| 1963 | + |
| 1964 | + try: |
| 1965 | + product_codes = euca.get_instance_product_codes().split('\n' |
| 1966 | + ) |
| 1967 | + except MetadataReadError: |
| 1968 | + print 'Unable to read product codes' |
| 1969 | + |
| 1970 | + try: |
| 1971 | + ancestor_ami_ids = euca.get_ancestor_ami_ids().split('\n') |
| 1972 | + except MetadataReadError: |
| 1973 | + print 'Unable to read product codes' |
| 1974 | + except IOError: |
| 1975 | + |
| 1976 | + print 'Unable to read instance metadata. Pass the --no-inherit option if you wish to exclude instance metadata.' |
| 1977 | + sys.exit() |
| 1978 | + |
| 1979 | + return (ramdisk_id, kernel_id, block_dev_mapping, product_codes, |
| 1980 | + ancestor_ami_ids) |
| 1981 | + |
| 1982 | + |
| 1983 | +def add_product_codes(product_code_string, product_codes): |
| 1984 | + if not product_codes: |
| 1985 | + product_codes = [] |
| 1986 | + product_code_values = product_code_string.split(',') |
| 1987 | + |
| 1988 | + for p in product_code_values: |
| 1989 | + product_codes.append(p) |
| 1990 | + |
| 1991 | + return product_codes |
| 1992 | + |
| 1993 | + |
| 1994 | +def cleanup(path): |
| 1995 | + if os.path.exists(path): |
| 1996 | + os.remove(path) |
| 1997 | + |
| 1998 | + |
| 1999 | +def main(): |
| 2000 | + euca = None |
| 2001 | + try: |
| 2002 | + euca = Euca2ool('a:c:k:u:B:d:br:p:s:v:e:', [ |
| 2003 | + 'cert=', |
| 2004 | + 'privatekey=', |
| 2005 | + 'user=', |
| 2006 | + 'prefix=', |
| 2007 | + 'volume=', |
| 2008 | + 'all', |
| 2009 | + 'kernel=', |
| 2010 | + 'ramdisk=', |
| 2011 | + 'block-device-mapping=', |
| 2012 | + 'destination=', |
| 2013 | + 'ec2cert=', |
| 2014 | + 'arch=', |
| 2015 | + 'size=', |
| 2016 | + 'exclude=', |
| 2017 | + 'inherit=', |
| 2018 | + 'no-inherit', |
| 2019 | + 'batch', |
| 2020 | + 'fstab=', |
| 2021 | + 'generate-fstab', |
| 2022 | + 'productcodes=', |
| 2023 | + ]) |
| 2024 | + except Exception, e: |
| 2025 | + print e |
| 2026 | + usage() |
| 2027 | + |
| 2028 | + kernel = None |
| 2029 | + user = None |
| 2030 | + ramdisk = None |
| 2031 | + try: |
| 2032 | + cert_path = euca.get_environ('EC2_CERT') |
| 2033 | + private_key_path = euca.get_environ('EC2_PRIVATE_KEY') |
| 2034 | + ec2cert_path = euca.get_environ('EUCALYPTUS_CERT') |
| 2035 | + user_string = euca.get_environ('EC2_USER_ID') |
| 2036 | + except NotFoundError: |
| 2037 | + sys.exit(1) |
| 2038 | + |
| 2039 | + prefix = 'image' |
| 2040 | + size_in_MB = 10 * 1024 |
| 2041 | + destination_path = '/disk1' |
| 2042 | + target_arch = 'x86_64' |
| 2043 | + mapping = None |
| 2044 | + excludes_string = None |
| 2045 | + excludes = None |
| 2046 | + all = False |
| 2047 | + volume_path = '/' |
| 2048 | + inherit = True |
| 2049 | + product_codes = None |
| 2050 | + ancestor_ami_ids = None |
| 2051 | + fstab_path = None |
| 2052 | + generate_fstab = False |
| 2053 | + product_code_string = None |
| 2054 | + if user_string: |
| 2055 | + try: |
| 2056 | + user = int(user_string) |
| 2057 | + except ValueError: |
| 2058 | + print 'Invalid user', user_string |
| 2059 | + sys.exit() |
| 2060 | + user = user_string |
| 2061 | + |
| 2062 | + for (name, value) in euca.opts: |
| 2063 | + if name in ('-h', '--help'): |
| 2064 | + usage(0) |
| 2065 | + elif name in ('-c', '--cert'): |
| 2066 | + cert_path = value |
| 2067 | + elif name in ('-k', '--privatekey'): |
| 2068 | + private_key_path = value |
| 2069 | + elif name in ('-u', '--user'): |
| 2070 | + try: |
| 2071 | + value = value.replace('-', '') |
| 2072 | + user = int(value) |
| 2073 | + except ValueError: |
| 2074 | + print 'Invalid user', value |
| 2075 | + sys.exit() |
| 2076 | + user = value |
| 2077 | + elif name == '--kernel': |
| 2078 | + kernel = value |
| 2079 | + elif name == '--ramdisk': |
| 2080 | + ramdisk = value |
| 2081 | + elif name in ('-p', '--prefix'): |
| 2082 | + prefix = value |
| 2083 | + elif name in ('-s', '--size'): |
| 2084 | + size_in_MB = int(value) |
| 2085 | + elif name in ('-v', '--volume'): |
| 2086 | + volume_path = value |
| 2087 | + elif name in ('-e', '--exclude'): |
| 2088 | + excludes_string = value |
| 2089 | + elif name in ('-d', '--destination'): |
| 2090 | + destination_path = value |
| 2091 | + elif name == '--ec2cert': |
| 2092 | + ec2cert_path = value |
| 2093 | + elif name in ('-a', '--all'): |
| 2094 | + all = True |
| 2095 | + elif name in ('-r', '--arch'): |
| 2096 | + target_arch = value |
| 2097 | + if target_arch != 'i386' and target_arch != 'x86_64': |
| 2098 | + print 'target architecture must be i386 or x86_64' |
| 2099 | + usage() |
| 2100 | + elif name in ('-B', '--block-device-mapping'): |
| 2101 | + mapping = value |
| 2102 | + elif name == '--no-inherit': |
| 2103 | + inherit = False |
| 2104 | + elif name == '--generate-fstab': |
| 2105 | + generate_fstab = True |
| 2106 | + elif name == '--fstab': |
| 2107 | + fstab_path = value |
| 2108 | + elif name == '--productcodes': |
| 2109 | + product_code_string = value |
| 2110 | + elif name == '--version': |
| 2111 | + version() |
| 2112 | + |
| 2113 | + if size_in_MB and cert_path and private_key_path and user \ |
| 2114 | + and ec2cert_path: |
| 2115 | + try: |
| 2116 | + euca.validate_file(cert_path) |
| 2117 | + except FileValidationError: |
| 2118 | + print 'Invalid cert' |
| 2119 | + sys.exit(1) |
| 2120 | + try: |
| 2121 | + euca.validate_file(private_key_path) |
| 2122 | + except FileValidationError: |
| 2123 | + print 'Invalid private key' |
| 2124 | + sys.exit(1) |
| 2125 | + try: |
| 2126 | + euca.validate_file(ec2cert_path) |
| 2127 | + except FileValidationError: |
| 2128 | + print 'Invalid ec2cert' |
| 2129 | + sys.exit(1) |
| 2130 | + try: |
| 2131 | + euca.validate_dir(volume_path) |
| 2132 | + except DirValidationError: |
| 2133 | + print 'Invalid directory', volume_path |
| 2134 | + sys.exit(1) |
| 2135 | + if generate_fstab and fstab_path: |
| 2136 | + print '--generate-fstab and --fstab path cannot both be set.' |
| 2137 | + sys.exit(1) |
| 2138 | + if fstab_path: |
| 2139 | + try: |
| 2140 | + euca.validate_file(fstab_path) |
| 2141 | + except FileValidationError: |
| 2142 | + print 'Invalid fstab path' |
| 2143 | + sys.exit(1) |
| 2144 | + if not fstab_path: |
| 2145 | + if platform.machine() == 'i386': |
| 2146 | + fstab_path = 'old' |
| 2147 | + else: |
| 2148 | + fstab_path = 'new' |
| 2149 | + |
| 2150 | + check_root() |
| 2151 | + check_image_size(size_in_MB) |
| 2152 | + volume_path = os.path.normpath(volume_path) |
| 2153 | + if not all: |
| 2154 | + excludes = parse_excludes(excludes_string) |
| 2155 | + euca.add_excludes(volume_path, excludes) |
| 2156 | + if inherit: |
| 2157 | + (ramdisk, kernel, mapping, product_codes, |
| 2158 | + ancestor_ami_ids) = get_instance_metadata(euca, ramdisk, |
| 2159 | + kernel, mapping) |
| 2160 | + if product_code_string: |
| 2161 | + product_codes = add_product_codes(product_code_string, |
| 2162 | + product_codes) |
| 2163 | + try: |
| 2164 | + image_path = euca.make_image(size_in_MB, excludes, prefix, |
| 2165 | + destination_path) |
| 2166 | + except NotFoundError: |
| 2167 | + sys.exit(1) |
| 2168 | + except UnsupportedException: |
| 2169 | + sys.exit(1) |
| 2170 | + image_path = os.path.normpath(image_path) |
| 2171 | + if image_path.find(volume_path) == 0: |
| 2172 | + exclude_image = image_path.replace(volume_path, '', 1) |
| 2173 | + image_path_parts = exclude_image.split('/') |
| 2174 | + if len(image_path_parts) > 1: |
| 2175 | + exclude_image = \ |
| 2176 | + exclude_image.replace(image_path_parts[0] + '/', '' |
| 2177 | + , 1) |
| 2178 | + excludes.append(exclude_image) |
| 2179 | + try: |
| 2180 | + euca.copy_volume(image_path, volume_path, excludes, |
| 2181 | + generate_fstab, fstab_path) |
| 2182 | + except CopyError: |
| 2183 | + print 'Unable to copy files' |
| 2184 | + cleanup(image_path) |
| 2185 | + sys.exit(1) |
| 2186 | + except NotFoundError: |
| 2187 | + cleanup(image_path) |
| 2188 | + sys.exit(1) |
| 2189 | + except CommandFailed: |
| 2190 | + cleanup(image_path) |
| 2191 | + sys.exit(1) |
| 2192 | + except UnsupportedException: |
| 2193 | + cleanup(image_path) |
| 2194 | + sys.exit(1) |
| 2195 | + |
| 2196 | + (image_size, sha_image_digest) = euca.check_image(image_path, |
| 2197 | + destination_path) |
| 2198 | + if not prefix: |
| 2199 | + prefix = euca.get_relative_filename(image_path) |
| 2200 | + try: |
| 2201 | + tgz_file = euca.tarzip_image(prefix, image_path, |
| 2202 | + destination_path) |
| 2203 | + except NotFoundError: |
| 2204 | + sys.exit(1) |
| 2205 | + except CommandFailed: |
| 2206 | + sys.exit(1) |
| 2207 | + |
| 2208 | + (encrypted_file, key, iv, bundled_size) = \ |
| 2209 | + euca.encrypt_image(tgz_file) |
| 2210 | + os.remove(tgz_file) |
| 2211 | + (parts, parts_digest) = euca.split_image(encrypted_file) |
| 2212 | + euca.generate_manifest( |
| 2213 | + destination_path, |
| 2214 | + prefix, |
| 2215 | + parts, |
| 2216 | + parts_digest, |
| 2217 | + image_path, |
| 2218 | + key, |
| 2219 | + iv, |
| 2220 | + cert_path, |
| 2221 | + ec2cert_path, |
| 2222 | + private_key_path, |
| 2223 | + target_arch, |
| 2224 | + image_size, |
| 2225 | + bundled_size, |
| 2226 | + sha_image_digest, |
| 2227 | + user, |
| 2228 | + kernel, |
| 2229 | + ramdisk, |
| 2230 | + mapping, |
| 2231 | + product_codes, |
| 2232 | + ancestor_ami_ids, |
| 2233 | + ) |
| 2234 | + os.remove(encrypted_file) |
| 2235 | + else: |
| 2236 | + |
| 2237 | +# ....cleanup(image_path) |
| 2238 | + |
| 2239 | + if not size_in_MB: |
| 2240 | + print 'size must be specified.' |
| 2241 | + if not cert_path: |
| 2242 | + print 'cert must be specified.' |
| 2243 | + if not private_key_path: |
| 2244 | + print 'privatekey must be specified.' |
| 2245 | + if not user: |
| 2246 | + print 'user must be specified.' |
| 2247 | + if not ec2cert_path: |
| 2248 | + defcert = "/usr/share/euca2ools/cert-ec2.pem" |
| 2249 | + if os.path.exists(defcert): |
| 2250 | + ec2cert_path = defcert |
| 2251 | + else: |
| 2252 | + print 'ec2cert must be specified or exist in %s.' % defcert |
| 2253 | + usage() |
| 2254 | + |
| 2255 | + |
| 2256 | +if __name__ == '__main__': |
| 2257 | + main() |
| 2258 | + |
| 2259 | |
| 2260 | === added directory '.pc/bundle-vol-use-ec2-cert-by-default.patch' |
| 2261 | === added file '.pc/bundle-vol-use-ec2-cert-by-default.patch/.timestamp' |
| 2262 | === added directory '.pc/bundle-vol-use-ec2-cert-by-default.patch/bin' |
| 2263 | === added file '.pc/bundle-vol-use-ec2-cert-by-default.patch/bin/euca-bundle-vol' |
| 2264 | --- .pc/bundle-vol-use-ec2-cert-by-default.patch/bin/euca-bundle-vol 1970-01-01 00:00:00 +0000 |
| 2265 | +++ .pc/bundle-vol-use-ec2-cert-by-default.patch/bin/euca-bundle-vol 2010-11-17 22:00:42 +0000 |
| 2266 | @@ -0,0 +1,452 @@ |
| 2267 | +#!/usr/bin/python |
| 2268 | +# -*- coding: utf-8 -*- |
| 2269 | + |
| 2270 | +# Software License Agreement (BSD License) |
| 2271 | +# |
| 2272 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 2273 | +# All rights reserved. |
| 2274 | +# |
| 2275 | +# Redistribution and use of this software in source and binary forms, with or |
| 2276 | +# without modification, are permitted provided that the following conditions |
| 2277 | +# are met: |
| 2278 | +# |
| 2279 | +# Redistributions of source code must retain the above |
| 2280 | +# copyright notice, this list of conditions and the |
| 2281 | +# following disclaimer. |
| 2282 | +# |
| 2283 | +# Redistributions in binary form must reproduce the above |
| 2284 | +# copyright notice, this list of conditions and the |
| 2285 | +# following disclaimer in the documentation and/or other |
| 2286 | +# materials provided with the distribution. |
| 2287 | +# |
| 2288 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 2289 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 2290 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 2291 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 2292 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 2293 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 2294 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 2295 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 2296 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 2297 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 2298 | +# POSSIBILITY OF SUCH DAMAGE. |
| 2299 | +# |
| 2300 | +# Author: Neil Soman neil@eucalyptus.com |
| 2301 | + |
| 2302 | +import getopt |
| 2303 | +import sys |
| 2304 | +import os |
| 2305 | +from euca2ools import Euca2ool, FileValidationError, \ |
| 2306 | + DirValidationError, CopyError, MetadataReadError, Util, \ |
| 2307 | + NotFoundError, CommandFailed, UnsupportedException |
| 2308 | +from subprocess import * |
| 2309 | +import platform |
| 2310 | + |
| 2311 | +usage_string = \ |
| 2312 | + """ |
| 2313 | +Bundle the local filesystem of a running instance as a bundled image. |
| 2314 | + |
| 2315 | +euca-bundle-vol -u, --user user -s, --size size_in_MB |
| 2316 | +[-c, --cert cert_path] [-k, --privatekey private_key_path] |
| 2317 | +[-a, --all] [-e, --exclude dir1, dir2,...dirN] [-p, --prefix prefix] [--[no-]inherit] [-v, --volume volume_path] [--fstab fstab_path] [--generate-fstab] [--kernel kernel_id] [--ramdisk ramdisk_id] [-B, --block-device-mapping mapping] |
| 2318 | +[-d, --destination destination_path] [--ec2cert ec2cert_path] [-r, --arch target_architecture] [--batch] [--version] |
| 2319 | + |
| 2320 | +REQUIRED PARAMETERS |
| 2321 | + |
| 2322 | +-u, --user User ID (12-digit) of the user who is bundling the image. |
| 2323 | + |
| 2324 | +-s, --size Size for the image in MB (default: 10GB or 10240MB). |
| 2325 | + |
| 2326 | +OPTIONAL PARAMETERS |
| 2327 | + |
| 2328 | +-c, --cert Path to the user's PEM encoded certificate. |
| 2329 | + |
| 2330 | +-k, --privatekey Path to the user's PEM encoded private key. |
| 2331 | + |
| 2332 | +-a, --all Bundle all directories (including mounted filesystems). |
| 2333 | + |
| 2334 | +-p, --prefix The prefix for the bundle image files. (default: image name). |
| 2335 | + |
| 2336 | +--[no-]inherit Add (or do not add) instance metadata to the bundled image. Inherit is set by default. |
| 2337 | + |
| 2338 | +-e, --exclude comma-separated list of directories to exclude. |
| 2339 | + |
| 2340 | +--kernel The kernel to be associated with the bundled image. |
| 2341 | + |
| 2342 | +--ramdisk The ramdisk to be associated with the bundled image. |
| 2343 | + |
| 2344 | +-B, --block-device-mapping Default block device mapping for the image (comma-separated list of key=value pairs). |
| 2345 | + |
| 2346 | +-d, --destination Directory to store the bundled image in (default: "/tmp"). Recommended. |
| 2347 | + |
| 2348 | +--ec2cert The path to the Cloud's X509 public key certificate. |
| 2349 | + |
| 2350 | +-r, --arch Target architecture for the image ('x86_64' or 'i386' default: 'x86_64'). |
| 2351 | + |
| 2352 | +-v, --volume Path to mounted volume to create the bundle from (default: "/"). |
| 2353 | + |
| 2354 | +--fstab Path to the fstab to be bundled into the image. |
| 2355 | + |
| 2356 | +--generate-fstab Generate fstab to bundle into the image. |
| 2357 | + |
| 2358 | +--batch Run in batch mode (compatibility only. has no effect). |
| 2359 | +""" |
| 2360 | + |
| 2361 | +MAX_IMAGE_SIZE = 1024 * 10 |
| 2362 | + |
| 2363 | + |
| 2364 | +def usage(status=1): |
| 2365 | + print usage_string |
| 2366 | + sys.exit(status) |
| 2367 | + |
| 2368 | + |
| 2369 | +def version(): |
| 2370 | + print Util().version() |
| 2371 | + sys.exit() |
| 2372 | + |
| 2373 | + |
| 2374 | +def check_root(): |
| 2375 | + if os.geteuid() == 0: |
| 2376 | + return |
| 2377 | + else: |
| 2378 | + print 'Must be superuser to execute this command.' |
| 2379 | + sys.exit() |
| 2380 | + |
| 2381 | + |
| 2382 | +def check_image_size(size): |
| 2383 | + if size > MAX_IMAGE_SIZE: |
| 2384 | + print 'Image Size is too large (Max = %d MB)' % MAX_IMAGE_SIZE |
| 2385 | + sys.exit() |
| 2386 | + |
| 2387 | + |
| 2388 | +def parse_excludes(excludes_string): |
| 2389 | + excludes = [] |
| 2390 | + if excludes_string: |
| 2391 | + excludes_string = excludes_string.replace(' ', '') |
| 2392 | + excludes = excludes_string.split(',') |
| 2393 | + return excludes |
| 2394 | + |
| 2395 | + |
| 2396 | +def get_instance_metadata( |
| 2397 | + euca, |
| 2398 | + ramdisk, |
| 2399 | + kernel, |
| 2400 | + mapping, |
| 2401 | + ): |
| 2402 | + product_codes = None |
| 2403 | + ramdisk_id = ramdisk |
| 2404 | + kernel_id = kernel |
| 2405 | + block_dev_mapping = mapping |
| 2406 | + ancestor_ami_ids = None |
| 2407 | + try: |
| 2408 | + euca.can_read_instance_metadata() |
| 2409 | + if not ramdisk_id: |
| 2410 | + try: |
| 2411 | + ramdisk_id = euca.get_instance_ramdisk() |
| 2412 | + except MetadataReadError: |
| 2413 | + print 'Unable to read ramdisk id' |
| 2414 | + |
| 2415 | + if not kernel_id: |
| 2416 | + try: |
| 2417 | + kernel_id = euca.get_instance_kernel() |
| 2418 | + except MetadataReadError: |
| 2419 | + print 'Unable to read kernel id' |
| 2420 | + |
| 2421 | + if not block_dev_mapping: |
| 2422 | + try: |
| 2423 | + block_dev_mapping = \ |
| 2424 | + euca.get_instance_block_device_mappings() |
| 2425 | + except MetadataReadError: |
| 2426 | + print 'Unable to read block device mapping' |
| 2427 | + |
| 2428 | + try: |
| 2429 | + product_codes = euca.get_instance_product_codes().split('\n' |
| 2430 | + ) |
| 2431 | + except MetadataReadError: |
| 2432 | + print 'Unable to read product codes' |
| 2433 | + |
| 2434 | + try: |
| 2435 | + ancestor_ami_ids = euca.get_ancestor_ami_ids().split('\n') |
| 2436 | + except MetadataReadError: |
| 2437 | + print 'Unable to read product codes' |
| 2438 | + except IOError: |
| 2439 | + |
| 2440 | + print 'Unable to read instance metadata. Pass the --no-inherit option if you wish to exclude instance metadata.' |
| 2441 | + sys.exit() |
| 2442 | + |
| 2443 | + return (ramdisk_id, kernel_id, block_dev_mapping, product_codes, |
| 2444 | + ancestor_ami_ids) |
| 2445 | + |
| 2446 | + |
| 2447 | +def add_product_codes(product_code_string, product_codes): |
| 2448 | + if not product_codes: |
| 2449 | + product_codes = [] |
| 2450 | + product_code_values = product_code_string.split(',') |
| 2451 | + |
| 2452 | + for p in product_code_values: |
| 2453 | + product_codes.append(p) |
| 2454 | + |
| 2455 | + return product_codes |
| 2456 | + |
| 2457 | + |
| 2458 | +def cleanup(path): |
| 2459 | + if os.path.exists(path): |
| 2460 | + os.remove(path) |
| 2461 | + |
| 2462 | + |
| 2463 | +def main(): |
| 2464 | + euca = None |
| 2465 | + try: |
| 2466 | + euca = Euca2ool('a:c:k:u:B:d:br:p:s:v:e:', [ |
| 2467 | + 'cert=', |
| 2468 | + 'privatekey=', |
| 2469 | + 'user=', |
| 2470 | + 'prefix=', |
| 2471 | + 'volume=', |
| 2472 | + 'all', |
| 2473 | + 'kernel=', |
| 2474 | + 'ramdisk=', |
| 2475 | + 'block-device-mapping=', |
| 2476 | + 'destination=', |
| 2477 | + 'ec2cert=', |
| 2478 | + 'arch=', |
| 2479 | + 'size=', |
| 2480 | + 'exclude=', |
| 2481 | + 'inherit=', |
| 2482 | + 'no-inherit', |
| 2483 | + 'batch', |
| 2484 | + 'fstab=', |
| 2485 | + 'generate-fstab', |
| 2486 | + 'productcodes=', |
| 2487 | + ]) |
| 2488 | + except Exception, e: |
| 2489 | + print e |
| 2490 | + usage() |
| 2491 | + |
| 2492 | + kernel = None |
| 2493 | + user = None |
| 2494 | + ramdisk = None |
| 2495 | + try: |
| 2496 | + cert_path = euca.get_environ('EC2_CERT') |
| 2497 | + private_key_path = euca.get_environ('EC2_PRIVATE_KEY') |
| 2498 | + ec2cert_path = euca.get_environ('EUCALYPTUS_CERT') |
| 2499 | + user_string = euca.get_environ('EC2_USER_ID') |
| 2500 | + except NotFoundError: |
| 2501 | + sys.exit(1) |
| 2502 | + |
| 2503 | + prefix = 'image' |
| 2504 | + size_in_MB = 10 * 1024 |
| 2505 | + destination_path = '/disk1' |
| 2506 | + target_arch = 'x86_64' |
| 2507 | + mapping = None |
| 2508 | + excludes_string = None |
| 2509 | + excludes = None |
| 2510 | + all = False |
| 2511 | + volume_path = '/' |
| 2512 | + inherit = True |
| 2513 | + product_codes = None |
| 2514 | + ancestor_ami_ids = None |
| 2515 | + fstab_path = None |
| 2516 | + generate_fstab = False |
| 2517 | + product_code_string = None |
| 2518 | + if user_string: |
| 2519 | + try: |
| 2520 | + user = int(user_string) |
| 2521 | + except ValueError: |
| 2522 | + print 'Invalid user', user_string |
| 2523 | + sys.exit() |
| 2524 | + user = user_string |
| 2525 | + |
| 2526 | + for (name, value) in euca.opts: |
| 2527 | + if name in ('-h', '--help'): |
| 2528 | + usage(0) |
| 2529 | + elif name in ('-c', '--cert'): |
| 2530 | + cert_path = value |
| 2531 | + elif name in ('-k', '--privatekey'): |
| 2532 | + private_key_path = value |
| 2533 | + elif name in ('-u', '--user'): |
| 2534 | + try: |
| 2535 | + value = value.replace('-', '') |
| 2536 | + user = int(value) |
| 2537 | + except ValueError: |
| 2538 | + print 'Invalid user', value |
| 2539 | + sys.exit() |
| 2540 | + user = value |
| 2541 | + elif name == '--kernel': |
| 2542 | + kernel = value |
| 2543 | + elif name == '--ramdisk': |
| 2544 | + ramdisk = value |
| 2545 | + elif name in ('-p', '--prefix'): |
| 2546 | + prefix = value |
| 2547 | + elif name in ('-s', '--size'): |
| 2548 | + size_in_MB = int(value) |
| 2549 | + elif name in ('-v', '--volume'): |
| 2550 | + volume_path = value |
| 2551 | + elif name in ('-e', '--exclude'): |
| 2552 | + excludes_string = value |
| 2553 | + elif name in ('-d', '--destination'): |
| 2554 | + destination_path = value |
| 2555 | + elif name == '--ec2cert': |
| 2556 | + ec2cert_path = value |
| 2557 | + elif name in ('-a', '--all'): |
| 2558 | + all = True |
| 2559 | + elif name in ('-r', '--arch'): |
| 2560 | + target_arch = value |
| 2561 | + if target_arch != 'i386' and target_arch != 'x86_64': |
| 2562 | + print 'target architecture must be i386 or x86_64' |
| 2563 | + usage() |
| 2564 | + elif name in ('-B', '--block-device-mapping'): |
| 2565 | + mapping = value |
| 2566 | + elif name == '--no-inherit': |
| 2567 | + inherit = False |
| 2568 | + elif name == '--generate-fstab': |
| 2569 | + generate_fstab = True |
| 2570 | + elif name == '--fstab': |
| 2571 | + fstab_path = value |
| 2572 | + elif name == '--productcodes': |
| 2573 | + product_code_string = value |
| 2574 | + elif name == '--version': |
| 2575 | + version() |
| 2576 | + |
| 2577 | + if size_in_MB and cert_path and private_key_path and user \ |
| 2578 | + and ec2cert_path: |
| 2579 | + try: |
| 2580 | + euca.validate_file(cert_path) |
| 2581 | + except FileValidationError: |
| 2582 | + print 'Invalid cert' |
| 2583 | + sys.exit(1) |
| 2584 | + try: |
| 2585 | + euca.validate_file(private_key_path) |
| 2586 | + except FileValidationError: |
| 2587 | + print 'Invalid private key' |
| 2588 | + sys.exit(1) |
| 2589 | + try: |
| 2590 | + euca.validate_file(ec2cert_path) |
| 2591 | + except FileValidationError: |
| 2592 | + print 'Invalid ec2cert' |
| 2593 | + sys.exit(1) |
| 2594 | + try: |
| 2595 | + euca.validate_dir(volume_path) |
| 2596 | + except DirValidationError: |
| 2597 | + print 'Invalid directory', volume_path |
| 2598 | + sys.exit(1) |
| 2599 | + if generate_fstab and fstab_path: |
| 2600 | + print '--generate-fstab and --fstab path cannot both be set.' |
| 2601 | + sys.exit(1) |
| 2602 | + if fstab_path: |
| 2603 | + try: |
| 2604 | + euca.validate_file(fstab_path) |
| 2605 | + except FileValidationError: |
| 2606 | + print 'Invalid fstab path' |
| 2607 | + sys.exit(1) |
| 2608 | + if not fstab_path: |
| 2609 | + if platform.machine() == 'i386': |
| 2610 | + fstab_path = 'old' |
| 2611 | + else: |
| 2612 | + fstab_path = 'new' |
| 2613 | + |
| 2614 | + check_root() |
| 2615 | + check_image_size(size_in_MB) |
| 2616 | + volume_path = os.path.normpath(volume_path) |
| 2617 | + if not all: |
| 2618 | + excludes = parse_excludes(excludes_string) |
| 2619 | + euca.add_excludes(volume_path, excludes) |
| 2620 | + if inherit: |
| 2621 | + (ramdisk, kernel, mapping, product_codes, |
| 2622 | + ancestor_ami_ids) = get_instance_metadata(euca, ramdisk, |
| 2623 | + kernel, mapping) |
| 2624 | + if product_code_string: |
| 2625 | + product_codes = add_product_codes(product_code_string, |
| 2626 | + product_codes) |
| 2627 | + try: |
| 2628 | + image_path = euca.make_image(size_in_MB, excludes, prefix, |
| 2629 | + destination_path) |
| 2630 | + except NotFoundError: |
| 2631 | + sys.exit(1) |
| 2632 | + except UnsupportedException: |
| 2633 | + sys.exit(1) |
| 2634 | + image_path = os.path.normpath(image_path) |
| 2635 | + if image_path.find(volume_path) == 0: |
| 2636 | + exclude_image = image_path.replace(volume_path, '', 1) |
| 2637 | + image_path_parts = exclude_image.split('/') |
| 2638 | + if len(image_path_parts) > 1: |
| 2639 | + exclude_image = \ |
| 2640 | + exclude_image.replace(image_path_parts[0] + '/', '' |
| 2641 | + , 1) |
| 2642 | + excludes.append(exclude_image) |
| 2643 | + try: |
| 2644 | + euca.copy_volume(image_path, volume_path, excludes, |
| 2645 | + generate_fstab, fstab_path) |
| 2646 | + except CopyError: |
| 2647 | + print 'Unable to copy files' |
| 2648 | + cleanup(image_path) |
| 2649 | + sys.exit(1) |
| 2650 | + except NotFoundError: |
| 2651 | + cleanup(image_path) |
| 2652 | + sys.exit(1) |
| 2653 | + except CommandFailed: |
| 2654 | + cleanup(image_path) |
| 2655 | + sys.exit(1) |
| 2656 | + except UnsupportedException: |
| 2657 | + cleanup(image_path) |
| 2658 | + sys.exit(1) |
| 2659 | + |
| 2660 | + (image_size, sha_image_digest) = euca.check_image(image_path, |
| 2661 | + destination_path) |
| 2662 | + if not prefix: |
| 2663 | + prefix = euca.get_relative_filename(image_path) |
| 2664 | + try: |
| 2665 | + tgz_file = euca.tarzip_image(prefix, image_path, |
| 2666 | + destination_path) |
| 2667 | + except NotFoundError: |
| 2668 | + sys.exit(1) |
| 2669 | + except CommandFailed: |
| 2670 | + sys.exit(1) |
| 2671 | + |
| 2672 | + (encrypted_file, key, iv, bundled_size) = \ |
| 2673 | + euca.encrypt_image(tgz_file) |
| 2674 | + os.remove(tgz_file) |
| 2675 | + (parts, parts_digest) = euca.split_image(encrypted_file) |
| 2676 | + euca.generate_manifest( |
| 2677 | + destination_path, |
| 2678 | + prefix, |
| 2679 | + parts, |
| 2680 | + parts_digest, |
| 2681 | + image_path, |
| 2682 | + key, |
| 2683 | + iv, |
| 2684 | + cert_path, |
| 2685 | + ec2cert_path, |
| 2686 | + private_key_path, |
| 2687 | + target_arch, |
| 2688 | + image_size, |
| 2689 | + bundled_size, |
| 2690 | + sha_image_digest, |
| 2691 | + user, |
| 2692 | + kernel, |
| 2693 | + ramdisk, |
| 2694 | + mapping, |
| 2695 | + product_codes, |
| 2696 | + ancestor_ami_ids, |
| 2697 | + ) |
| 2698 | + os.remove(encrypted_file) |
| 2699 | + else: |
| 2700 | + |
| 2701 | +# ....cleanup(image_path) |
| 2702 | + |
| 2703 | + if not size_in_MB: |
| 2704 | + print 'size must be specified.' |
| 2705 | + if not cert_path: |
| 2706 | + print 'cert must be specified.' |
| 2707 | + if not private_key_path: |
| 2708 | + print 'privatekey must be specified.' |
| 2709 | + if not user: |
| 2710 | + print 'user must be specified.' |
| 2711 | + if not ec2cert_path: |
| 2712 | + print 'ec2cert must be specified.' |
| 2713 | + usage() |
| 2714 | + |
| 2715 | + |
| 2716 | +if __name__ == '__main__': |
| 2717 | + main() |
| 2718 | + |
| 2719 | |
| 2720 | === added directory '.pc/describe-image-attribute-empty-kernel-ramdisk.patch' |
| 2721 | === added file '.pc/describe-image-attribute-empty-kernel-ramdisk.patch/.timestamp' |
| 2722 | === added directory '.pc/describe-image-attribute-empty-kernel-ramdisk.patch/bin' |
| 2723 | === added file '.pc/describe-image-attribute-empty-kernel-ramdisk.patch/bin/euca-describe-image-attribute' |
| 2724 | --- .pc/describe-image-attribute-empty-kernel-ramdisk.patch/bin/euca-describe-image-attribute 1970-01-01 00:00:00 +0000 |
| 2725 | +++ .pc/describe-image-attribute-empty-kernel-ramdisk.patch/bin/euca-describe-image-attribute 2010-11-17 22:00:42 +0000 |
| 2726 | @@ -0,0 +1,166 @@ |
| 2727 | +#!/usr/bin/python |
| 2728 | +# -*- coding: utf-8 -*- |
| 2729 | + |
| 2730 | +# Software License Agreement (BSD License) |
| 2731 | +# |
| 2732 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 2733 | +# All rights reserved. |
| 2734 | +# |
| 2735 | +# Redistribution and use of this software in source and binary forms, with or |
| 2736 | +# without modification, are permitted provided that the following conditions |
| 2737 | +# are met: |
| 2738 | +# |
| 2739 | +# Redistributions of source code must retain the above |
| 2740 | +# copyright notice, this list of conditions and the |
| 2741 | +# following disclaimer. |
| 2742 | +# |
| 2743 | +# Redistributions in binary form must reproduce the above |
| 2744 | +# copyright notice, this list of conditions and the |
| 2745 | +# following disclaimer in the documentation and/or other |
| 2746 | +# materials provided with the distribution. |
| 2747 | +# |
| 2748 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 2749 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 2750 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 2751 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 2752 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 2753 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 2754 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 2755 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 2756 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 2757 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 2758 | +# POSSIBILITY OF SUCH DAMAGE. |
| 2759 | +# |
| 2760 | +# Author: Neil Soman neil@eucalyptus.com |
| 2761 | + |
| 2762 | +import getopt |
| 2763 | +import sys |
| 2764 | +import os |
| 2765 | +from euca2ools import Euca2ool, Util, ConnectionFailed |
| 2766 | + |
| 2767 | +usage_string = \ |
| 2768 | + """ |
| 2769 | +Show image attributes. |
| 2770 | + |
| 2771 | +euca-describe-image-attribute [-l, --launch-permission] [-p, --product-code] |
| 2772 | +[-B, --block-device-mapping] [--kernel] [--ramdisk] |
| 2773 | +[-h, --help] [--version] [--debug] image_id |
| 2774 | + |
| 2775 | +REQUIRED PARAMETERS |
| 2776 | + |
| 2777 | +image_id unique identifier for the image that you want to retrieve the attributes for. |
| 2778 | + |
| 2779 | +OPTIONAL PARAMETERS |
| 2780 | + |
| 2781 | +-B, --block-device-mapping show block device mapping. |
| 2782 | +-l, --launch-permission show launch permissions. |
| 2783 | +-p, --product-code show the product codes associated with the image |
| 2784 | +--kernel show the kernel id associated with the image. |
| 2785 | +--ramdisk show the ramdisk id associated with the image. |
| 2786 | + |
| 2787 | +""" |
| 2788 | + |
| 2789 | + |
| 2790 | +def usage(status=1): |
| 2791 | + print usage_string |
| 2792 | + Util().usage() |
| 2793 | + sys.exit(status) |
| 2794 | + |
| 2795 | + |
| 2796 | +def version(): |
| 2797 | + print Util().version() |
| 2798 | + sys.exit() |
| 2799 | + |
| 2800 | + |
| 2801 | +def display_image_attribute(image_id, image_attribute): |
| 2802 | + if image_attribute.name == 'launch_permission': |
| 2803 | + if image_attribute.attrs.has_key('groups'): |
| 2804 | + for group in image_attribute.attrs['groups']: |
| 2805 | + print 'launchPermission\t%s\tgroup\t%s' \ |
| 2806 | + % (image_attribute.image_id, group) |
| 2807 | + if image_attribute.attrs.has_key('user_ids'): |
| 2808 | + for userid in image_attribute.attrs['user_ids']: |
| 2809 | + print 'launchPermission\t%s\tuserId\t%s' \ |
| 2810 | + % (image_attribute.image_id, userid) |
| 2811 | + if image_attribute.attrs.has_key('product_codes'): |
| 2812 | + for product_code in image_attribute.attrs['product_codes']: |
| 2813 | + print 'productCodes\t%s\tproductCode\t%s' \ |
| 2814 | + % (image_attribute.image_id, product_code) |
| 2815 | + if image_attribute.kernel is not None: |
| 2816 | + print 'kernel\t%s\t\t%s' % (image_attribute.image_id, |
| 2817 | + image_attribute.value) |
| 2818 | + if image_attribute.ramdisk is not None: |
| 2819 | + print 'ramdisk\t%s\t\t%s' % (image_attribute.image_id, |
| 2820 | + image_attribute.value) |
| 2821 | + if image_attribute.attrs.has_key('block_device_mapping'): |
| 2822 | + block_device_mapping = \ |
| 2823 | + image_attribute.attrs['block_device_mapping'] |
| 2824 | + for dev_name in block_device_mapping: |
| 2825 | + print 'blockDeviceMapping\t%s\tblockDeviceMap\t%s: %s' \ |
| 2826 | + % (image_id, dev_name, |
| 2827 | + block_device_mapping[dev_name]) |
| 2828 | + |
| 2829 | +def main(): |
| 2830 | + euca = None |
| 2831 | + try: |
| 2832 | + euca = Euca2ool('lpB', ['block-device-mapping', |
| 2833 | + 'launch-permission', 'product-code', 'kernel', |
| 2834 | + 'ramdisk']) |
| 2835 | + except Exception, e: |
| 2836 | + print e |
| 2837 | + usage() |
| 2838 | + |
| 2839 | + image_id = None |
| 2840 | + image_attribute = None |
| 2841 | + |
| 2842 | + for (name, value) in euca.opts: |
| 2843 | + if name in ('-p', '--product-code'): |
| 2844 | + if not image_attribute: |
| 2845 | + image_attribute = 'productCodes' |
| 2846 | + elif name in ('-l', '--launch-permission'): |
| 2847 | + if not image_attribute: |
| 2848 | + image_attribute = 'launchPermission' |
| 2849 | + elif name in ('-B', '--block-device-mapping'): |
| 2850 | + if not image_attribute: |
| 2851 | + image_attribute = 'blockDeviceMapping' |
| 2852 | + elif name == '--kernel': |
| 2853 | + if not image_attribute: |
| 2854 | + image_attribute = 'kernel' |
| 2855 | + elif name == '--ramdisk': |
| 2856 | + if not image_attribute: |
| 2857 | + image_attribute = 'ramdisk' |
| 2858 | + elif name in ('-h', '--help'): |
| 2859 | + usage(0) |
| 2860 | + elif name == '--version': |
| 2861 | + version() |
| 2862 | + |
| 2863 | + for arg in euca.args: |
| 2864 | + image_id = arg |
| 2865 | + break |
| 2866 | + |
| 2867 | + if image_id and image_attribute: |
| 2868 | + try: |
| 2869 | + euca_conn = euca.make_connection() |
| 2870 | + except ConnectionFailed, e: |
| 2871 | + print e.message |
| 2872 | + sys.exit(1) |
| 2873 | + try: |
| 2874 | + image_attribute = \ |
| 2875 | + euca_conn.get_image_attribute(image_id=image_id, |
| 2876 | + attribute=image_attribute) |
| 2877 | + except Exception, ex: |
| 2878 | + euca.display_error_and_exit('%s' % ex) |
| 2879 | + |
| 2880 | + if image_attribute: |
| 2881 | + display_image_attribute(image_id, image_attribute) |
| 2882 | + else: |
| 2883 | + if not image_id: |
| 2884 | + print 'image_id must be specified' |
| 2885 | + if not image_attribute: |
| 2886 | + print 'image attribute must be specified' |
| 2887 | + usage() |
| 2888 | + |
| 2889 | + |
| 2890 | +if __name__ == '__main__': |
| 2891 | + main() |
| 2892 | + |
| 2893 | |
| 2894 | === added directory '.pc/describe-images-return-results-like-ec2-describe-images.patch' |
| 2895 | === added file '.pc/describe-images-return-results-like-ec2-describe-images.patch/.timestamp' |
| 2896 | === added directory '.pc/describe-images-return-results-like-ec2-describe-images.patch/bin' |
| 2897 | === added file '.pc/describe-images-return-results-like-ec2-describe-images.patch/bin/euca-describe-images' |
| 2898 | --- .pc/describe-images-return-results-like-ec2-describe-images.patch/bin/euca-describe-images 1970-01-01 00:00:00 +0000 |
| 2899 | +++ .pc/describe-images-return-results-like-ec2-describe-images.patch/bin/euca-describe-images 2010-11-17 22:00:42 +0000 |
| 2900 | @@ -0,0 +1,162 @@ |
| 2901 | +#!/usr/bin/python |
| 2902 | +# -*- coding: utf-8 -*- |
| 2903 | + |
| 2904 | +# Software License Agreement (BSD License) |
| 2905 | +# |
| 2906 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 2907 | +# All rights reserved. |
| 2908 | +# |
| 2909 | +# Redistribution and use of this software in source and binary forms, with or |
| 2910 | +# without modification, are permitted provided that the following conditions |
| 2911 | +# are met: |
| 2912 | +# |
| 2913 | +# Redistributions of source code must retain the above |
| 2914 | +# copyright notice, this list of conditions and the |
| 2915 | +# following disclaimer. |
| 2916 | +# |
| 2917 | +# Redistributions in binary form must reproduce the above |
| 2918 | +# copyright notice, this list of conditions and the |
| 2919 | +# following disclaimer in the documentation and/or other |
| 2920 | +# materials provided with the distribution. |
| 2921 | +# |
| 2922 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 2923 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 2924 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 2925 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 2926 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 2927 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 2928 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 2929 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 2930 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 2931 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 2932 | +# POSSIBILITY OF SUCH DAMAGE. |
| 2933 | +# |
| 2934 | +# Author: Neil Soman neil@eucalyptus.com |
| 2935 | + |
| 2936 | +import getopt |
| 2937 | +import sys |
| 2938 | +import os |
| 2939 | +from euca2ools import Euca2ool, Util, ConnectionFailed |
| 2940 | + |
| 2941 | +usage_string = \ |
| 2942 | + """ |
| 2943 | +Shows information about machine images. |
| 2944 | + |
| 2945 | +euca-describe-images [-a] [-o owner] [-x user] [-h, --help] [--version] [--debug] [image1 image2 ... imageN] |
| 2946 | + |
| 2947 | +OPTIONAL PARAMETERS |
| 2948 | + |
| 2949 | +image1 image2 ... imageN Images to describe. |
| 2950 | + |
| 2951 | +-a Show all images that the user has access to. |
| 2952 | + |
| 2953 | +-o Show only images owned by the owner specified are displayed. |
| 2954 | + |
| 2955 | +-x Show only images that the specified user is permitted to launch. |
| 2956 | + |
| 2957 | +""" |
| 2958 | + |
| 2959 | + |
| 2960 | +def usage(status=1): |
| 2961 | + print usage_string |
| 2962 | + Util().usage(compat=True) |
| 2963 | + sys.exit(status) |
| 2964 | + |
| 2965 | + |
| 2966 | +def version(): |
| 2967 | + print Util().version() |
| 2968 | + sys.exit() |
| 2969 | + |
| 2970 | + |
| 2971 | +def display_images(images, image_ids): |
| 2972 | + check_image_ids = False |
| 2973 | + if len(image_ids) > 0: |
| 2974 | + check_image_ids = True |
| 2975 | + for image in images: |
| 2976 | + if check_image_ids: |
| 2977 | + if not image.id in image_ids: |
| 2978 | + continue |
| 2979 | + image_string = '%s\t%s\t%s\t%s' % (image.id, image.location, |
| 2980 | + image.ownerId, image.state) |
| 2981 | + if image.is_public: |
| 2982 | + image_string += '\tpublic' |
| 2983 | + else: |
| 2984 | + image_string += '\tprivate' |
| 2985 | + |
| 2986 | + image_string += '\t%s' % ','.join(image.product_codes) |
| 2987 | + |
| 2988 | + for i in [image.architecture, image.type, image.kernel_id, |
| 2989 | + image.ramdisk_id]: |
| 2990 | + image_string += '\t%s' % ((' ' if i == None else i)) |
| 2991 | + |
| 2992 | + if image.platform: |
| 2993 | + image_string += '\t%s' % image.platform |
| 2994 | + if image.root_device_type: |
| 2995 | + image_string += '\t%s' % image.root_device_type |
| 2996 | + print 'IMAGE\t%s' % image_string |
| 2997 | + if image.block_device_mapping: |
| 2998 | + block_dev_mapping = image.block_device_mapping |
| 2999 | + if image.root_device_type == 'ebs': |
| 3000 | + block_dev_string = '%s\t%s\t%s' \ |
| 3001 | + % (block_dev_mapping.current_name, |
| 3002 | + block_dev_mapping.current_value.snapshot_id, |
| 3003 | + block_dev_mapping.current_value.size) |
| 3004 | + print 'BLOCKDEVICEMAPPING\t%s' % block_dev_string |
| 3005 | + |
| 3006 | + |
| 3007 | +def main(): |
| 3008 | + euca = None |
| 3009 | + try: |
| 3010 | + euca = Euca2ool('ao:x:', compat=True) |
| 3011 | + except Exception, e: |
| 3012 | + print e |
| 3013 | + usage() |
| 3014 | + |
| 3015 | + show_all = False |
| 3016 | + executable_by = ['self'] |
| 3017 | + owners = ['self'] |
| 3018 | + defaults = True |
| 3019 | + |
| 3020 | + for (name, value) in euca.opts: |
| 3021 | + if name in ('-h', '--help'): |
| 3022 | + usage(0) |
| 3023 | + elif name == '-x': |
| 3024 | + if defaults: |
| 3025 | + executable_by = [] |
| 3026 | + defaults = False |
| 3027 | + owners = [] |
| 3028 | + executable_by.append(value) |
| 3029 | + elif name == '-o': |
| 3030 | + if defaults: |
| 3031 | + executable_by = [] |
| 3032 | + defaults = False |
| 3033 | + owners.append(value) |
| 3034 | + elif name == '-a': |
| 3035 | + executable_by = ['self', 'all'] |
| 3036 | + owners = [] |
| 3037 | + elif name == '--version': |
| 3038 | + version() |
| 3039 | + |
| 3040 | + image_ids = euca.process_args() |
| 3041 | + if defaults and len(image_ids) > 0: |
| 3042 | + executable_by = ['self', 'all'] |
| 3043 | + owners = [] |
| 3044 | + |
| 3045 | + try: |
| 3046 | + euca_conn = euca.make_connection() |
| 3047 | + except ConnectionFailed, e: |
| 3048 | + print e.message |
| 3049 | + sys.exit(1) |
| 3050 | + |
| 3051 | + try: |
| 3052 | + images = euca_conn.get_all_images(image_ids=image_ids, |
| 3053 | + owners=owners, executable_by=executable_by) |
| 3054 | + except Exception, ex: |
| 3055 | + euca.display_error_and_exit('%s' % ex) |
| 3056 | + |
| 3057 | + display_images(images, image_ids) |
| 3058 | + |
| 3059 | + |
| 3060 | +if __name__ == '__main__': |
| 3061 | + main() |
| 3062 | + |
| 3063 | |
| 3064 | === added directory '.pc/download-bundle-fix-usage.patch' |
| 3065 | === added file '.pc/download-bundle-fix-usage.patch/.timestamp' |
| 3066 | === added directory '.pc/download-bundle-fix-usage.patch/bin' |
| 3067 | === added file '.pc/download-bundle-fix-usage.patch/bin/euca-download-bundle' |
| 3068 | --- .pc/download-bundle-fix-usage.patch/bin/euca-download-bundle 1970-01-01 00:00:00 +0000 |
| 3069 | +++ .pc/download-bundle-fix-usage.patch/bin/euca-download-bundle 2010-11-17 22:00:42 +0000 |
| 3070 | @@ -0,0 +1,189 @@ |
| 3071 | +#!/usr/bin/python |
| 3072 | +# -*- coding: utf-8 -*- |
| 3073 | + |
| 3074 | +# Software License Agreement (BSD License) |
| 3075 | +# |
| 3076 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 3077 | +# All rights reserved. |
| 3078 | +# |
| 3079 | +# Redistribution and use of this software in source and binary forms, with or |
| 3080 | +# without modification, are permitted provided that the following conditions |
| 3081 | +# are met: |
| 3082 | +# |
| 3083 | +# Redistributions of source code must retain the above |
| 3084 | +# copyright notice, this list of conditions and the |
| 3085 | +# following disclaimer. |
| 3086 | +# |
| 3087 | +# Redistributions in binary form must reproduce the above |
| 3088 | +# copyright notice, this list of conditions and the |
| 3089 | +# following disclaimer in the documentation and/or other |
| 3090 | +# materials provided with the distribution. |
| 3091 | +# |
| 3092 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 3093 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 3094 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 3095 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 3096 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 3097 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 3098 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 3099 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 3100 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 3101 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 3102 | +# POSSIBILITY OF SUCH DAMAGE. |
| 3103 | +# |
| 3104 | +# Author: Neil Soman neil@eucalyptus.com |
| 3105 | + |
| 3106 | +import getopt |
| 3107 | +import sys |
| 3108 | +import os |
| 3109 | +from xml.dom import minidom |
| 3110 | +from euca2ools import Euca2ool, FileValidationError, Util, \ |
| 3111 | + ConnectionFailed |
| 3112 | +from boto.exception import S3ResponseError |
| 3113 | +from boto.s3 import Connection |
| 3114 | +from boto.s3.key import Key |
| 3115 | + |
| 3116 | +usage_string = \ |
| 3117 | + """ |
| 3118 | +Downloads a bundled image from a bucket. |
| 3119 | + |
| 3120 | +euca-download-bundle -b, --bucket bucket [-m, --manifest manifest_path] [-d, --directory directory] |
| 3121 | +[-h, --help] [--version] [--debug] |
| 3122 | + |
| 3123 | +REQUIRED PARAMETERS |
| 3124 | + |
| 3125 | +-b, --bucket The name of the bucket to download to. Bucket will be created if it does not exist. |
| 3126 | + |
| 3127 | +OPTIONAL PARAMETERS |
| 3128 | + |
| 3129 | +-m, --manifest_path The path to the manifest file. |
| 3130 | + |
| 3131 | +-d, --directory The name of the directory to download the bundled parts to. |
| 3132 | + |
| 3133 | +""" |
| 3134 | + |
| 3135 | + |
| 3136 | +def usage(status=1): |
| 3137 | + print usage_string |
| 3138 | + Util().usage() |
| 3139 | + sys.exit(status) |
| 3140 | + |
| 3141 | + |
| 3142 | +def version(): |
| 3143 | + print Util().version() |
| 3144 | + sys.exit() |
| 3145 | + |
| 3146 | + |
| 3147 | +def ensure_bucket(connection, bucket): |
| 3148 | + bucket_instance = None |
| 3149 | + try: |
| 3150 | + bucket_instance = connection.get_bucket(bucket) |
| 3151 | + except S3ResponseError, s3error: |
| 3152 | + print 'Unable to get bucket %s' % bucket |
| 3153 | + sys.exit() |
| 3154 | + return bucket_instance |
| 3155 | + |
| 3156 | + |
| 3157 | +def get_parts(manifest_filename): |
| 3158 | + parts = [] |
| 3159 | + dom = minidom.parse(manifest_filename) |
| 3160 | + manifest_elem = dom.getElementsByTagName('manifest')[0] |
| 3161 | + parts_list = manifest_elem.getElementsByTagName('filename') |
| 3162 | + for part_elem in parts_list: |
| 3163 | + nodes = part_elem.childNodes |
| 3164 | + for node in nodes: |
| 3165 | + if node.nodeType == node.TEXT_NODE: |
| 3166 | + parts.append(node.data) |
| 3167 | + return parts |
| 3168 | + |
| 3169 | + |
| 3170 | +def get_relative_filename(filename): |
| 3171 | + f_parts = filename.split('/') |
| 3172 | + return f_parts[len(f_parts) - 1] |
| 3173 | + |
| 3174 | + |
| 3175 | +def get_manifests(bucket): |
| 3176 | + manifests = [] |
| 3177 | + keys = bucket.get_all_keys() |
| 3178 | + for k in keys: |
| 3179 | + if k.name: |
| 3180 | + if k.name.find('manifest') >= 0: |
| 3181 | + manifests.append(k.name) |
| 3182 | + return manifests |
| 3183 | + |
| 3184 | + |
| 3185 | +def download_manifests(bucket, manifests, directory): |
| 3186 | + if len(manifests) > 0: |
| 3187 | + if not os.path.exists(directory): |
| 3188 | + os.makedirs(directory) |
| 3189 | + for manifest in manifests: |
| 3190 | + k = Key(bucket) |
| 3191 | + k.key = manifest |
| 3192 | + print 'Downloading', manifest |
| 3193 | + manifest_file = open(os.path.join(directory, manifest), 'wb') |
| 3194 | + k.get_contents_to_file(manifest_file) |
| 3195 | + manifest_file.close() |
| 3196 | + |
| 3197 | + |
| 3198 | +def download_parts(bucket, manifests, directory): |
| 3199 | + for manifest in manifests: |
| 3200 | + manifest_filename = os.path.join(directory, manifest) |
| 3201 | + parts = get_parts(manifest_filename) |
| 3202 | + for part in parts: |
| 3203 | + k = Key(bucket) |
| 3204 | + k.key = part |
| 3205 | + print 'Downloading', part |
| 3206 | + part_file = open(os.path.join(directory, part), 'wb') |
| 3207 | + k.get_contents_to_file(part_file) |
| 3208 | + part_file.close() |
| 3209 | + |
| 3210 | + |
| 3211 | +def main(): |
| 3212 | + euca = None |
| 3213 | + try: |
| 3214 | + euca = Euca2ool('b:m:d:', ['bucket=', 'manifest=', 'directory=' |
| 3215 | + ], is_s3=True) |
| 3216 | + except Exception, e: |
| 3217 | + print e |
| 3218 | + usage() |
| 3219 | + |
| 3220 | + bucket = None |
| 3221 | + manifest_path = None |
| 3222 | + directory = '/tmp' |
| 3223 | + |
| 3224 | + for (name, value) in euca.opts: |
| 3225 | + if name in ('-h', '--help'): |
| 3226 | + usage(0) |
| 3227 | + elif name in ('-d', '--directory'): |
| 3228 | + directory = value |
| 3229 | + elif name in ('-b', '--bucket'): |
| 3230 | + bucket = value |
| 3231 | + elif name in ('-m', '--manifest'): |
| 3232 | + manifest_path = value |
| 3233 | + |
| 3234 | + if bucket: |
| 3235 | + if manifest_path: |
| 3236 | + try: |
| 3237 | + euca.validate_file(manifest_path) |
| 3238 | + except FileValidationError: |
| 3239 | + print 'Invalid manifest', manifest_path |
| 3240 | + sys.exit(1) |
| 3241 | + |
| 3242 | + try: |
| 3243 | + conn = euca.make_connection() |
| 3244 | + except ConnectionFailed, e: |
| 3245 | + print e.message |
| 3246 | + sys.exit(1) |
| 3247 | + |
| 3248 | + bucket_instance = ensure_bucket(conn, bucket) |
| 3249 | + manifests = get_manifests(bucket_instance) |
| 3250 | + download_manifests(bucket_instance, manifests, directory) |
| 3251 | + download_parts(bucket_instance, manifests, directory) |
| 3252 | + else: |
| 3253 | + print 'bucket must be specified.' |
| 3254 | + usage() |
| 3255 | + |
| 3256 | + |
| 3257 | +if __name__ == '__main__': |
| 3258 | + main() |
| 3259 | + |
| 3260 | |
| 3261 | === added directory '.pc/download-bundle-fix-usage.patch/man' |
| 3262 | === added file '.pc/download-bundle-fix-usage.patch/man/euca-download-bundle.1' |
| 3263 | --- .pc/download-bundle-fix-usage.patch/man/euca-download-bundle.1 1970-01-01 00:00:00 +0000 |
| 3264 | +++ .pc/download-bundle-fix-usage.patch/man/euca-download-bundle.1 2010-11-17 22:00:42 +0000 |
| 3265 | @@ -0,0 +1,72 @@ |
| 3266 | +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. |
| 3267 | +.TH BUCKET "1" "July 2010" "bucket must be specified." "User Commands" |
| 3268 | +.SH NAME |
| 3269 | +bucket \- Eucalyptus tool: Downloads a bundled image from a bucket. |
| 3270 | +.SH DESCRIPTION |
| 3271 | +Downloads a bundled image from a bucket. |
| 3272 | +.PP |
| 3273 | +euca\-download\-bundle \fB\-b\fR, \fB\-\-bucket\fR bucket [\-m, \fB\-\-manifest\fR manifest_path] [\-d, \fB\-\-directory\fR directory] |
| 3274 | +[\-h, \fB\-\-help]\fR [\-\-version] [\-\-debug] |
| 3275 | +.PP |
| 3276 | +REQUIRED PARAMETERS |
| 3277 | +.PP |
| 3278 | + |
| 3279 | +\fB\-b\fR, \fB\-\-bucket\fR The name of the bucket to download to. Bucket will be created if it does not exist. |
| 3280 | +.PP |
| 3281 | +OPTIONAL PARAMETERS |
| 3282 | +.PP |
| 3283 | +\fB\-m\fR, \fB\-\-manifest_path\fR The path to the manifest file. |
| 3284 | +.PP |
| 3285 | +\fB\-d\fR, \fB\-\-directory\fR The name of the directory to download the bundled parts to. |
| 3286 | +.PP |
| 3287 | +\fB\-a\fR, \fB\-\-access\-key\fR User's Access Key ID. |
| 3288 | +.PP |
| 3289 | +\fB\-s\fR, \fB\-\-secret\-key\fR User's Secret Key. |
| 3290 | +.PP |
| 3291 | +\fB\-U\fR, \fB\-\-url\fR URL of the Cloud to connect to. |
| 3292 | +.PP |
| 3293 | +\fB\-\-config\fR Read credentials and cloud settings from the |
| 3294 | +.IP |
| 3295 | +specified config file (defaults to $HOME/.eucarc or /etc/euca2ools/eucarc). |
| 3296 | +.PP |
| 3297 | +\fB\-h\fR, \fB\-\-help\fR Display this help message. |
| 3298 | +.PP |
| 3299 | +\fB\-\-version\fR Display the version of this tool. |
| 3300 | +.PP |
| 3301 | +\fB\-\-debug\fR Turn on debugging. |
| 3302 | +.PP |
| 3303 | +Euca2ools will use the environment variables EC2_URL, EC2_ACCESS_KEY, EC2_SECRET_KEY, EC2_CERT, EC2_PRIVATE_KEY, S3_URL, EUCALYPTUS_CERT by default. |
| 3304 | +.PP |
| 3305 | +Downloads a bundled image from a bucket. |
| 3306 | +.PP |
| 3307 | +euca\-download\-bundle \fB\-b\fR, \fB\-\-bucket\fR bucket [\-m, \fB\-\-manifest\fR manifest_path] [\-d, \fB\-\-directory\fR directory] |
| 3308 | +[\-h, \fB\-\-help]\fR [\-\-version] [\-\-debug] |
| 3309 | +.PP |
| 3310 | +REQUIRED PARAMETERS |
| 3311 | +.PP |
| 3312 | + |
| 3313 | +\fB\-b\fR, \fB\-\-bucket\fR The name of the bucket to download to. Bucket will be created if it does not exist. |
| 3314 | +.PP |
| 3315 | +OPTIONAL PARAMETERS |
| 3316 | +.PP |
| 3317 | +\fB\-m\fR, \fB\-\-manifest_path\fR The path to the manifest file. |
| 3318 | +.PP |
| 3319 | +\fB\-d\fR, \fB\-\-directory\fR The name of the directory to download the bundled parts to. |
| 3320 | +.PP |
| 3321 | +\fB\-a\fR, \fB\-\-access\-key\fR User's Access Key ID. |
| 3322 | +.PP |
| 3323 | +\fB\-s\fR, \fB\-\-secret\-key\fR User's Secret Key. |
| 3324 | +.PP |
| 3325 | +\fB\-U\fR, \fB\-\-url\fR URL of the Cloud to connect to. |
| 3326 | +.PP |
| 3327 | +\fB\-\-config\fR Read credentials and cloud settings from the |
| 3328 | +.IP |
| 3329 | +specified config file (defaults to $HOME/.eucarc or /etc/euca2ools/eucarc). |
| 3330 | +.PP |
| 3331 | +\fB\-h\fR, \fB\-\-help\fR Display this help message. |
| 3332 | +.PP |
| 3333 | +\fB\-\-version\fR Display the version of this tool. |
| 3334 | +.PP |
| 3335 | +\fB\-\-debug\fR Turn on debugging. |
| 3336 | +.PP |
| 3337 | +Euca2ools will use the environment variables EC2_URL, EC2_ACCESS_KEY, EC2_SECRET_KEY, EC2_CERT, EC2_PRIVATE_KEY, S3_URL, EUCALYPTUS_CERT by default. |
| 3338 | |
| 3339 | === added directory '.pc/euca-revoke-exit-on-usage.patch' |
| 3340 | === added file '.pc/euca-revoke-exit-on-usage.patch/.timestamp' |
| 3341 | === added directory '.pc/euca-revoke-exit-on-usage.patch/bin' |
| 3342 | === added file '.pc/euca-revoke-exit-on-usage.patch/bin/euca-revoke' |
| 3343 | --- .pc/euca-revoke-exit-on-usage.patch/bin/euca-revoke 1970-01-01 00:00:00 +0000 |
| 3344 | +++ .pc/euca-revoke-exit-on-usage.patch/bin/euca-revoke 2010-11-17 22:00:42 +0000 |
| 3345 | @@ -0,0 +1,202 @@ |
| 3346 | +#!/usr/bin/python |
| 3347 | +# -*- coding: utf-8 -*- |
| 3348 | + |
| 3349 | +# Software License Agreement (BSD License) |
| 3350 | +# |
| 3351 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 3352 | +# All rights reserved. |
| 3353 | +# |
| 3354 | +# Redistribution and use of this software in source and binary forms, with or |
| 3355 | +# without modification, are permitted provided that the following conditions |
| 3356 | +# are met: |
| 3357 | +# |
| 3358 | +# Redistributions of source code must retain the above |
| 3359 | +# copyright notice, this list of conditions and the |
| 3360 | +# following disclaimer. |
| 3361 | +# |
| 3362 | +# Redistributions in binary form must reproduce the above |
| 3363 | +# copyright notice, this list of conditions and the |
| 3364 | +# following disclaimer in the documentation and/or other |
| 3365 | +# materials provided with the distribution. |
| 3366 | +# |
| 3367 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 3368 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 3369 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 3370 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 3371 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 3372 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 3373 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 3374 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 3375 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 3376 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 3377 | +# POSSIBILITY OF SUCH DAMAGE. |
| 3378 | +# |
| 3379 | +# Author: Neil Soman neil@eucalyptus.com |
| 3380 | + |
| 3381 | +import getopt |
| 3382 | +import sys |
| 3383 | +import os |
| 3384 | +from euca2ools import Euca2ool, AddressValidationError, \ |
| 3385 | + ProtocolValidationError, Util, ConnectionFailed |
| 3386 | + |
| 3387 | +usage_string = \ |
| 3388 | + """ |
| 3389 | +Revoke a rule for a security group. |
| 3390 | + |
| 3391 | +euca-revoke [-P | --protocol protocol] [-p | --port-range port_range] |
| 3392 | +[-t | --icmp-type-code type:code] [-o | --source-group source_group] |
| 3393 | +[-u | --source-group-user source_group_user] [-s | --source-subnet source_subnet] |
| 3394 | +[-h, --help] [--version] [--debug] group_name |
| 3395 | + |
| 3396 | +REQUIRED PARAMETERS |
| 3397 | + |
| 3398 | +group_name Name of the group to add the rule to. |
| 3399 | + |
| 3400 | +OPTIONAL PARAMETERS |
| 3401 | + |
| 3402 | +-P, --protocol Protocol ("tcp" "udp" or "icmp"). |
| 3403 | + |
| 3404 | +-p, --port-range Range of ports for the rule (specified as "from-to"). |
| 3405 | + |
| 3406 | +-t, --icmp-type-code ICMP type and code specified as "type:code" |
| 3407 | + |
| 3408 | +-o, --source-group Group from which traffic is authorized by the rule. |
| 3409 | + |
| 3410 | +-u, --source-group-user User ID for the source group. |
| 3411 | + |
| 3412 | +-s, --source-subnet The source subnet for the rule. |
| 3413 | + |
| 3414 | +""" |
| 3415 | + |
| 3416 | + |
| 3417 | +def usage(status=1): |
| 3418 | + print usage_string |
| 3419 | + Util().usage(compat=True) |
| 3420 | + |
| 3421 | + |
| 3422 | +def version(): |
| 3423 | + print Util().version() |
| 3424 | + sys.exit(status) |
| 3425 | + |
| 3426 | + |
| 3427 | +def main(): |
| 3428 | + euca = None |
| 3429 | + try: |
| 3430 | + euca = Euca2ool('P:p:o:u:s:t:', [ |
| 3431 | + 'protocol=', |
| 3432 | + 'port-range=', |
| 3433 | + 'source-group=', |
| 3434 | + 'source-group-user=', |
| 3435 | + 'source-subnet=', |
| 3436 | + 'icmp-type-code=', |
| 3437 | + ], compat=True) |
| 3438 | + except Exception, e: |
| 3439 | + print e |
| 3440 | + usage() |
| 3441 | + |
| 3442 | + group_name = None |
| 3443 | + protocol = 'tcp' |
| 3444 | + from_port = None |
| 3445 | + to_port = None |
| 3446 | + source_group_name = None |
| 3447 | + source_group_owner_id = None |
| 3448 | + cidr_ip = None |
| 3449 | + |
| 3450 | + for (name, value) in euca.opts: |
| 3451 | + if name in ('-P', '--protocol'): |
| 3452 | + protocol = value |
| 3453 | + elif name in ('-p', '--port-range'): |
| 3454 | + ports = value.split('-') |
| 3455 | + if len(ports) > 1: |
| 3456 | + from_port = int(ports[0]) |
| 3457 | + to_port = int(ports[1]) |
| 3458 | + else: |
| 3459 | + from_port = to_port = int(ports[0]) |
| 3460 | + elif name in ('-o', '--source-group'): |
| 3461 | + source_group_name = value |
| 3462 | + protocol = None |
| 3463 | + elif name in ('-u', '--source-group-user'): |
| 3464 | + source_group_owner_id = value |
| 3465 | + elif name in ('-s', '--source-subnet'): |
| 3466 | + cidr_ip = value |
| 3467 | + elif name in ('-t', '--icmp-type-code'): |
| 3468 | + code_parts = value.split(':') |
| 3469 | + if len(code_parts) > 1: |
| 3470 | + try: |
| 3471 | + from_port = int(code_parts[0]) |
| 3472 | + to_port = int(code_parts[1]) |
| 3473 | + except ValueError: |
| 3474 | + print 'port must be an integer.' |
| 3475 | + sys.exit(1) |
| 3476 | + elif name in ('-h', '--help'): |
| 3477 | + usage(0) |
| 3478 | + elif name == '--version': |
| 3479 | + version() |
| 3480 | + |
| 3481 | + if source_group_name: |
| 3482 | + from_port = None |
| 3483 | + to_port = None |
| 3484 | + protocol = None |
| 3485 | + |
| 3486 | + for arg in euca.args: |
| 3487 | + group_name = arg |
| 3488 | + break |
| 3489 | + |
| 3490 | + if group_name: |
| 3491 | + if cidr_ip: |
| 3492 | + try: |
| 3493 | + euca.validate_address(cidr_ip) |
| 3494 | + except AddressValidationError: |
| 3495 | + print 'Invalid address', cidr_ip |
| 3496 | + sys.exit(1) |
| 3497 | + if protocol: |
| 3498 | + try: |
| 3499 | + euca.validate_protocol(protocol) |
| 3500 | + except ProtocolValidationError: |
| 3501 | + print 'Invalid protocol', protocol |
| 3502 | + sys.exit(1) |
| 3503 | + |
| 3504 | + try: |
| 3505 | + euca_conn = euca.make_connection() |
| 3506 | + except ConnectionFailed, e: |
| 3507 | + print e.message |
| 3508 | + sys.exit(1) |
| 3509 | + try: |
| 3510 | + return_code = euca_conn.revoke_security_group( |
| 3511 | + group_name=group_name, |
| 3512 | + src_security_group_name=source_group_name, |
| 3513 | + src_security_group_owner_id=source_group_owner_id, |
| 3514 | + ip_protocol=protocol, |
| 3515 | + from_port=from_port, |
| 3516 | + to_port=to_port, |
| 3517 | + cidr_ip=cidr_ip, |
| 3518 | + ) |
| 3519 | + except Exception, ex: |
| 3520 | + euca.display_error_and_exit('%s' % ex) |
| 3521 | + |
| 3522 | + if return_code: |
| 3523 | + print 'GROUP\t%s' % group_name |
| 3524 | + permission_string = 'PERMISSION\t%s\tALLOWS' % group_name |
| 3525 | + if protocol: |
| 3526 | + permission_string += '\t%s' % protocol |
| 3527 | + if from_port: |
| 3528 | + permission_string += '\t%s' % from_port |
| 3529 | + if to_port: |
| 3530 | + permission_string += '\t%s' % to_port |
| 3531 | + if source_group_owner_id: |
| 3532 | + permission_string += '\tUSER\t%s' \ |
| 3533 | + % source_group_owner_id |
| 3534 | + if source_group_name: |
| 3535 | + permission_string += '\tGRPNAME\t%s' % source_group_name |
| 3536 | + if cidr_ip: |
| 3537 | + permission_string += '\tFROM\tCIDR\t%s' % cidr_ip |
| 3538 | + print permission_string |
| 3539 | + else: |
| 3540 | + |
| 3541 | + print 'group_name must be specified' |
| 3542 | + usage() |
| 3543 | + |
| 3544 | + |
| 3545 | +if __name__ == '__main__': |
| 3546 | + main() |
| 3547 | + |
| 3548 | |
| 3549 | === added directory '.pc/print-instances-cleanup.patch' |
| 3550 | === added file '.pc/print-instances-cleanup.patch/.timestamp' |
| 3551 | === added directory '.pc/print-instances-cleanup.patch/bin' |
| 3552 | === added file '.pc/print-instances-cleanup.patch/bin/euca-describe-instances' |
| 3553 | --- .pc/print-instances-cleanup.patch/bin/euca-describe-instances 1970-01-01 00:00:00 +0000 |
| 3554 | +++ .pc/print-instances-cleanup.patch/bin/euca-describe-instances 2010-11-17 22:00:42 +0000 |
| 3555 | @@ -0,0 +1,156 @@ |
| 3556 | +#!/usr/bin/python |
| 3557 | +# -*- coding: utf-8 -*- |
| 3558 | + |
| 3559 | +# Software License Agreement (BSD License) |
| 3560 | +# |
| 3561 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 3562 | +# All rights reserved. |
| 3563 | +# |
| 3564 | +# Redistribution and use of this software in source and binary forms, with or |
| 3565 | +# without modification, are permitted provided that the following conditions |
| 3566 | +# are met: |
| 3567 | +# |
| 3568 | +# Redistributions of source code must retain the above |
| 3569 | +# copyright notice, this list of conditions and the |
| 3570 | +# following disclaimer. |
| 3571 | +# |
| 3572 | +# Redistributions in binary form must reproduce the above |
| 3573 | +# copyright notice, this list of conditions and the |
| 3574 | +# following disclaimer in the documentation and/or other |
| 3575 | +# materials provided with the distribution. |
| 3576 | +# |
| 3577 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 3578 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 3579 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 3580 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 3581 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 3582 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 3583 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 3584 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 3585 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 3586 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 3587 | +# POSSIBILITY OF SUCH DAMAGE. |
| 3588 | +# |
| 3589 | +# Author: Neil Soman neil@eucalyptus.com |
| 3590 | + |
| 3591 | +import getopt |
| 3592 | +import sys |
| 3593 | +import os |
| 3594 | +from euca2ools import Euca2ool, InstanceValidationError, Util, \ |
| 3595 | + ConnectionFailed |
| 3596 | + |
| 3597 | +usage_string = \ |
| 3598 | + """ |
| 3599 | +Shows information about instances. |
| 3600 | + |
| 3601 | +euca-describe-instances [-h, --help] [--version] [--debug] |
| 3602 | +[instance1... instanceN] |
| 3603 | + |
| 3604 | +OPTIONAL PARAMETERS |
| 3605 | + |
| 3606 | +instance1... instanceN instances to describe. |
| 3607 | + |
| 3608 | +""" |
| 3609 | + |
| 3610 | + |
| 3611 | +def usage(status=1): |
| 3612 | + print usage_string |
| 3613 | + Util().usage() |
| 3614 | + sys.exit(status) |
| 3615 | + |
| 3616 | + |
| 3617 | +def version(): |
| 3618 | + print Util().version() |
| 3619 | + sys.exit() |
| 3620 | + |
| 3621 | + |
| 3622 | +def display_reservations(reservations, instance_ids): |
| 3623 | + check_instance_ids = False |
| 3624 | + if len(instance_ids) > 0: |
| 3625 | + check_instance_ids = True |
| 3626 | + for reservation in reservations: |
| 3627 | + instances = [] |
| 3628 | + if check_instance_ids: |
| 3629 | + for instance in reservation.instances: |
| 3630 | + if instance.id in instance_ids: |
| 3631 | + instances.append(instance) |
| 3632 | + else: |
| 3633 | + instances = reservation.instances |
| 3634 | + if len(instances) == 0: |
| 3635 | + continue |
| 3636 | + reservation_string = '%s\t%s' % (reservation.id, |
| 3637 | + reservation.owner_id) |
| 3638 | + group_delim = '\t' |
| 3639 | + for group in reservation.groups: |
| 3640 | + reservation_string += '%s%s' % (group_delim, group.id) |
| 3641 | + group_delim = ', ' |
| 3642 | + print 'RESERVATION\t%s' % reservation_string |
| 3643 | + for instance in instances: |
| 3644 | + if instance: |
| 3645 | + instance_string = '%s\t%s\t%s\t%s\t%s' % (instance.id, |
| 3646 | + instance.image_id, instance.public_dns_name, |
| 3647 | + instance.private_dns_name, instance.state) |
| 3648 | + if instance.key_name: |
| 3649 | + instance_string += ' \t%s' % instance.key_name |
| 3650 | + if instance.ami_launch_index: |
| 3651 | + instance_string += ' \t%s' \ |
| 3652 | + % instance.ami_launch_index |
| 3653 | + if instance.product_codes: |
| 3654 | + first = True |
| 3655 | + for p in instance.product_codes: |
| 3656 | + if first: |
| 3657 | + instance_string += ' \t%s' % p |
| 3658 | + first = False |
| 3659 | + else: |
| 3660 | + instance_string += ',%s' % p |
| 3661 | + if instance.instance_type: |
| 3662 | + instance_string += ' \t%s' % instance.instance_type |
| 3663 | + if instance.launch_time: |
| 3664 | + instance_string += ' \t%s' % instance.launch_time |
| 3665 | + if instance.placement: |
| 3666 | + instance_string += ' \t%s' % instance.placement |
| 3667 | + if instance.kernel: |
| 3668 | + instance_string += ' \t%s' % instance.kernel |
| 3669 | + if instance.ramdisk: |
| 3670 | + instance_string += ' \t%s' % instance.ramdisk |
| 3671 | + print 'INSTANCE\t%s' % instance_string |
| 3672 | + |
| 3673 | + |
| 3674 | +def main(): |
| 3675 | + euca = None |
| 3676 | + try: |
| 3677 | + euca = Euca2ool() |
| 3678 | + except Exception, e: |
| 3679 | + print e |
| 3680 | + usage() |
| 3681 | + |
| 3682 | + for (name, value) in euca.opts: |
| 3683 | + if name in ('-h', '--help'): |
| 3684 | + usage(0) |
| 3685 | + elif name == '--version': |
| 3686 | + version() |
| 3687 | + |
| 3688 | + instance_ids = euca.process_args() |
| 3689 | + try: |
| 3690 | + for id in instance_ids: |
| 3691 | + euca.validate_instance_id(id) |
| 3692 | + except InstanceValidationError: |
| 3693 | + print 'Invalid instance id' |
| 3694 | + sys.exit(1) |
| 3695 | + |
| 3696 | + try: |
| 3697 | + euca_conn = euca.make_connection() |
| 3698 | + except ConnectionFailed, e: |
| 3699 | + print e.message |
| 3700 | + sys.exit(1) |
| 3701 | + try: |
| 3702 | + reservations = euca_conn.get_all_instances(instance_ids) |
| 3703 | + except Exception, ex: |
| 3704 | + euca.display_error_and_exit('%s' % ex) |
| 3705 | + |
| 3706 | + display_reservations(reservations, instance_ids) |
| 3707 | + |
| 3708 | + |
| 3709 | +if __name__ == '__main__': |
| 3710 | + main() |
| 3711 | + |
| 3712 | |
| 3713 | === added file '.pc/print-instances-cleanup.patch/bin/euca-run-instances' |
| 3714 | --- .pc/print-instances-cleanup.patch/bin/euca-run-instances 1970-01-01 00:00:00 +0000 |
| 3715 | +++ .pc/print-instances-cleanup.patch/bin/euca-run-instances 2010-11-17 22:00:42 +0000 |
| 3716 | @@ -0,0 +1,260 @@ |
| 3717 | +#!/usr/bin/python |
| 3718 | +# -*- coding: utf-8 -*- |
| 3719 | + |
| 3720 | +# Software License Agreement (BSD License) |
| 3721 | +# |
| 3722 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 3723 | +# All rights reserved. |
| 3724 | +# |
| 3725 | +# Redistribution and use of this software in source and binary forms, with or |
| 3726 | +# without modification, are permitted provided that the following conditions |
| 3727 | +# are met: |
| 3728 | +# |
| 3729 | +# Redistributions of source code must retain the above |
| 3730 | +# copyright notice, this list of conditions and the |
| 3731 | +# following disclaimer. |
| 3732 | +# |
| 3733 | +# Redistributions in binary form must reproduce the above |
| 3734 | +# copyright notice, this list of conditions and the |
| 3735 | +# following disclaimer in the documentation and/or other |
| 3736 | +# materials provided with the distribution. |
| 3737 | +# |
| 3738 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 3739 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 3740 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 3741 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 3742 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 3743 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 3744 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 3745 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 3746 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 3747 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 3748 | +# POSSIBILITY OF SUCH DAMAGE. |
| 3749 | +# |
| 3750 | +# Author: Neil Soman neil@eucalyptus.com |
| 3751 | + |
| 3752 | +import getopt |
| 3753 | +import sys |
| 3754 | +import os |
| 3755 | +from euca2ools import Euca2ool, Util, ConnectionFailed |
| 3756 | + |
| 3757 | +usage_string = \ |
| 3758 | + """ |
| 3759 | +Starts instances. |
| 3760 | + |
| 3761 | +euca-run-instances [-n, --instance-count count] [-g, --group group_name] [-k, --key keyname] |
| 3762 | +[-d user_data] [-f user_data_file] [--addressing addressing] [-t, --instance-type instance_type] |
| 3763 | +[-z, --availability-zone zone] [--kernel kernel_id] [--ramdisk ramdisk_id] [-b block_device_mapping] |
| 3764 | +[--monitor] [-s, --subnet subnet_id] |
| 3765 | +[-h, --help] [--version] [--debug] image_id |
| 3766 | + |
| 3767 | +REQUIRED PARAMETERS |
| 3768 | + |
| 3769 | +image_id identifier for the image to run. |
| 3770 | + |
| 3771 | +OPTIONAL PARAMETERS |
| 3772 | + |
| 3773 | +-n, --instance-count Number of instances to run. |
| 3774 | + |
| 3775 | +-g, --group Security group to run the instance under. |
| 3776 | + |
| 3777 | +-k, --key Name of a (previously created) keypair to associate with this reservation. |
| 3778 | +-d, --user-data User data for instances read from the command line. |
| 3779 | + |
| 3780 | +-f, --user-data-file User data for instances as a filename. |
| 3781 | + |
| 3782 | +--addressing Addressing mode (e.g., private). |
| 3783 | + |
| 3784 | +-t, --instance-type VM Image type to run the instance(s) as (default: m1.small). |
| 3785 | + |
| 3786 | +-z, --availability-zone Availability zone to run the instance(s) in. |
| 3787 | + |
| 3788 | +--kernel Id of the kernel to be used to launch instance(s). |
| 3789 | + |
| 3790 | +--ramdisk Id of the ramdisk to be used to launch instance(s). |
| 3791 | + |
| 3792 | +-b, --block-device-mapping Block device mapping for the instance(s). Option may be used multiple times. |
| 3793 | + |
| 3794 | +--monitor Enable monitoring for instance. |
| 3795 | + |
| 3796 | +-s, --subnet Amazon VPC subnet ID for the instance. |
| 3797 | + |
| 3798 | +""" |
| 3799 | + |
| 3800 | + |
| 3801 | +def usage(status=1): |
| 3802 | + print usage_string |
| 3803 | + Util().usage() |
| 3804 | + sys.exit(status) |
| 3805 | + |
| 3806 | + |
| 3807 | +def version(): |
| 3808 | + print Util().version() |
| 3809 | + sys.exit() |
| 3810 | + |
| 3811 | + |
| 3812 | +def display_reservations(reservation): |
| 3813 | + reservation_string = '%s\t%s' % (reservation.id, |
| 3814 | + reservation.owner_id) |
| 3815 | + group_delim = '\t' |
| 3816 | + for group in reservation.groups: |
| 3817 | + reservation_string += '%s%s' % (group_delim, group.id) |
| 3818 | + group_delim = ', ' |
| 3819 | + print 'RESERVATION\t%s' % reservation_string |
| 3820 | + for instance in reservation.instances: |
| 3821 | + instance_string = '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % ( |
| 3822 | + instance.id, |
| 3823 | + instance.image_id, |
| 3824 | + instance.public_dns_name, |
| 3825 | + instance.private_dns_name, |
| 3826 | + instance.state, |
| 3827 | + instance.key_name, |
| 3828 | + instance.launch_time, |
| 3829 | + instance.kernel, |
| 3830 | + instance.ramdisk, |
| 3831 | + ) |
| 3832 | + print 'INSTANCE\t%s' % instance_string |
| 3833 | + |
| 3834 | + |
| 3835 | +def read_user_data(user_data_filename): |
| 3836 | + USER_DATA_CHUNK_SIZE = 512 |
| 3837 | + user_data = '' |
| 3838 | + user_data_file = open(user_data_filename, 'r') |
| 3839 | + while 1: |
| 3840 | + data = user_data_file.read(USER_DATA_CHUNK_SIZE) |
| 3841 | + if not data: |
| 3842 | + break |
| 3843 | + user_data += data |
| 3844 | + user_data_file.close() |
| 3845 | + return user_data |
| 3846 | + |
| 3847 | + |
| 3848 | +def main(): |
| 3849 | + euca = None |
| 3850 | + try: |
| 3851 | + euca = Euca2ool('k:n:t:g:d:f:z:b:', [ |
| 3852 | + 'key=', |
| 3853 | + 'kernel=', |
| 3854 | + 'ramdisk=', |
| 3855 | + 'instance-count=', |
| 3856 | + 'instance-type=', |
| 3857 | + 'group=', |
| 3858 | + 'user-data=', |
| 3859 | + 'user-data-file=', |
| 3860 | + 'addressing=', |
| 3861 | + 'availability-zone=', |
| 3862 | + 'block-device-mapping=', |
| 3863 | + 'monitor', |
| 3864 | + 'subnet_id=', |
| 3865 | + ]) |
| 3866 | + except Exception, e: |
| 3867 | + print e |
| 3868 | + usage() |
| 3869 | + |
| 3870 | + image_id = None |
| 3871 | + keyname = None |
| 3872 | + kernel_id = None |
| 3873 | + ramdisk_id = None |
| 3874 | + min_count = 1 |
| 3875 | + max_count = 1 |
| 3876 | + instance_type = 'm1.small' |
| 3877 | + group_names = [] |
| 3878 | + user_data = None |
| 3879 | + user_data_file = None |
| 3880 | + addressing_type = None |
| 3881 | + zone = None |
| 3882 | + block_device_map_args = [] |
| 3883 | + block_device_map = None |
| 3884 | + monitor = False |
| 3885 | + subnet_id = None |
| 3886 | + for (name, value) in euca.opts: |
| 3887 | + if name in ('-k', '--key'): |
| 3888 | + keyname = value |
| 3889 | + elif name == '--kernel': |
| 3890 | + kernel_id = value |
| 3891 | + elif name == '--ramdisk': |
| 3892 | + ramdisk_id = value |
| 3893 | + elif name in ('-n', '--instance-count'): |
| 3894 | + counts = value.split('-') |
| 3895 | + try: |
| 3896 | + if (len(counts) > 1): |
| 3897 | + min_count = int(counts[0]) |
| 3898 | + max_count = int(counts[1]) |
| 3899 | + else: |
| 3900 | + min_count = max_count = int(counts[0]) |
| 3901 | + except ValueError: |
| 3902 | + print "Invalid value for --instance-count: ", value |
| 3903 | + sys.exit(1) |
| 3904 | + elif name in ('-t', '--instance-type'): |
| 3905 | + instance_type = value |
| 3906 | + elif name in ('-g', '--group'): |
| 3907 | + group_names.append(value) |
| 3908 | + elif name in ('-d', '--user-data'): |
| 3909 | + user_data = value |
| 3910 | + elif name in ('-f', '--user-data-file'): |
| 3911 | + user_data_file = value |
| 3912 | + elif name == '--addressing': |
| 3913 | + addressing_type = value |
| 3914 | + elif name in ('-z', '--availability-zone'): |
| 3915 | + zone = value |
| 3916 | + elif name in ('-b', '--block-device-mapping'): |
| 3917 | + block_device_map_args.append(value) |
| 3918 | + elif name == '--monitor': |
| 3919 | + monitor = True |
| 3920 | + elif name in ('-s', '--subnet'): |
| 3921 | + subnet_id = value |
| 3922 | + elif name in ('-h', '--help'): |
| 3923 | + usage(0) |
| 3924 | + elif name == '--version': |
| 3925 | + version() |
| 3926 | + |
| 3927 | + for arg in euca.args: |
| 3928 | + image_id = arg |
| 3929 | + break |
| 3930 | + |
| 3931 | + if image_id: |
| 3932 | + if not user_data: |
| 3933 | + if user_data_file: |
| 3934 | + try: |
| 3935 | + euca.validate_file(user_data_file) |
| 3936 | + except FileValidationError: |
| 3937 | + print 'Invalid user data file path' |
| 3938 | + sys.exit(1) |
| 3939 | + user_data = read_user_data(user_data_file) |
| 3940 | + try: |
| 3941 | + euca_conn = euca.make_connection() |
| 3942 | + except ConnectionFailed, e: |
| 3943 | + print e.message |
| 3944 | + sys.exit(1) |
| 3945 | + if block_device_map_args: |
| 3946 | + block_device_map = \ |
| 3947 | + euca.parse_block_device_args(block_device_map_args) |
| 3948 | + try: |
| 3949 | + reservation = euca_conn.run_instances( |
| 3950 | + image_id=image_id, |
| 3951 | + min_count=min_count, |
| 3952 | + max_count=max_count, |
| 3953 | + key_name=keyname, |
| 3954 | + security_groups=group_names, |
| 3955 | + user_data=user_data, |
| 3956 | + addressing_type=addressing_type, |
| 3957 | + instance_type=instance_type, |
| 3958 | + placement=zone, |
| 3959 | + kernel_id=kernel_id, |
| 3960 | + ramdisk_id=ramdisk_id, |
| 3961 | + block_device_map=block_device_map, |
| 3962 | + monitoring_enabled=monitor, |
| 3963 | + subnet_id=subnet_id |
| 3964 | + ) |
| 3965 | + except Exception, ex: |
| 3966 | + euca.display_error_and_exit('%s' % ex) |
| 3967 | + |
| 3968 | + display_reservations(reservation) |
| 3969 | + else: |
| 3970 | + print 'image_id must be specified' |
| 3971 | + usage() |
| 3972 | + |
| 3973 | + |
| 3974 | +if __name__ == '__main__': |
| 3975 | + main() |
| 3976 | + |
| 3977 | |
| 3978 | === added directory '.pc/print-instances-cleanup.patch/euca2ools' |
| 3979 | === added directory '.pc/print-instances-cleanup.patch/euca2ools/euca2ools' |
| 3980 | === added file '.pc/print-instances-cleanup.patch/euca2ools/euca2ools/__init__.py' |
| 3981 | --- .pc/print-instances-cleanup.patch/euca2ools/euca2ools/__init__.py 1970-01-01 00:00:00 +0000 |
| 3982 | +++ .pc/print-instances-cleanup.patch/euca2ools/euca2ools/__init__.py 2010-11-17 22:00:42 +0000 |
| 3983 | @@ -0,0 +1,1458 @@ |
| 3984 | +#!/usr/bin/python |
| 3985 | +# -*- coding: utf-8 -*- |
| 3986 | +# Software License Agreement (BSD License) |
| 3987 | +# |
| 3988 | +# Copyright (c) 2009, Eucalyptus Systems, Inc. |
| 3989 | +# All rights reserved. |
| 3990 | +# |
| 3991 | +# Redistribution and use of this software in source and binary forms, with or |
| 3992 | +# without modification, are permitted provided that the following conditions |
| 3993 | +# are met: |
| 3994 | +# |
| 3995 | +# Redistributions of source code must retain the above |
| 3996 | +# copyright notice, this list of conditions and the |
| 3997 | +# following disclaimer. |
| 3998 | +# |
| 3999 | +# Redistributions in binary form must reproduce the above |
| 4000 | +# copyright notice, this list of conditions and the |
| 4001 | +# following disclaimer in the documentation and/or other |
| 4002 | +# materials provided with the distribution. |
| 4003 | +# |
| 4004 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 4005 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 4006 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 4007 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 4008 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 4009 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 4010 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 4011 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 4012 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 4013 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 4014 | +# POSSIBILITY OF SUCH DAMAGE. |
| 4015 | +# |
| 4016 | +# Author: Neil Soman neil@eucalyptus.com |
| 4017 | + |
| 4018 | +import boto |
| 4019 | +import getopt |
| 4020 | +import sys |
| 4021 | +import os |
| 4022 | +import tarfile |
| 4023 | +from xml.dom.minidom import Document |
| 4024 | +from xml.dom import minidom |
| 4025 | +from hashlib import sha1 as sha |
| 4026 | +from M2Crypto import BN, EVP, RSA, X509 |
| 4027 | +from binascii import hexlify, unhexlify |
| 4028 | +from subprocess import * |
| 4029 | +import platform |
| 4030 | +import urllib |
| 4031 | +import re |
| 4032 | +import shutil |
| 4033 | +from boto.ec2.regioninfo import RegionInfo |
| 4034 | +from boto.ec2.blockdevicemapping import BlockDeviceMapping |
| 4035 | +from boto.ec2.blockdevicemapping import EBSBlockDeviceType |
| 4036 | +from boto.ec2.connection import EC2Connection |
| 4037 | +from boto.resultset import ResultSet |
| 4038 | +import logging |
| 4039 | +import base64 |
| 4040 | + |
| 4041 | +BUNDLER_NAME = 'euca-tools' |
| 4042 | +BUNDLER_VERSION = '1.3' |
| 4043 | +VERSION = '2007-10-10' |
| 4044 | +RELEASE = '31337' |
| 4045 | +AES = 'AES-128-CBC' |
| 4046 | + |
| 4047 | +IP_PROTOCOLS = ['tcp', 'udp', 'icmp'] |
| 4048 | + |
| 4049 | +IMAGE_IO_CHUNK = 10 * 1024 |
| 4050 | +IMAGE_SPLIT_CHUNK = IMAGE_IO_CHUNK * 1024 |
| 4051 | +MAX_LOOP_DEVS = 256 |
| 4052 | + |
| 4053 | +METADATA_URL = 'http://169.254.169.254/latest/meta-data/' |
| 4054 | + |
| 4055 | +# |
| 4056 | +# Monkey patch the SAX endElement handler in boto's |
| 4057 | +# BlockDeviceMapping class to not break on Eucalyptus's |
| 4058 | +# DescribeImageAttribute output. It's impossible to test |
| 4059 | +# this against EC2 because EC2 will not let you run a |
| 4060 | +# DescribeImageAttribute on the blockDeviceMapping attribute, |
| 4061 | +# even for an image you own. |
| 4062 | +# |
| 4063 | +def endElement(self, name, value, connection): |
| 4064 | + if name == 'virtualName': |
| 4065 | + self.current_vname = value |
| 4066 | + elif name == 'device' or name == 'deviceName': |
| 4067 | + if hasattr(self, 'current_vname') and self.current_vname: |
| 4068 | + self[self.current_vname] = value |
| 4069 | + self.current_vname = None |
| 4070 | + else: |
| 4071 | + self.current_name = value |
| 4072 | + |
| 4073 | +BlockDeviceMapping.endElement = endElement |
| 4074 | + |
| 4075 | +# |
| 4076 | +# Monkey patch the register_image method from EC2Connection |
| 4077 | +# The one in 1.9b required a value for the "name" parameter |
| 4078 | +# but in fact that parameter is only required for EBS-backed |
| 4079 | +# images. So, an EBS image needs a name but not a location |
| 4080 | +# and the S3 image needs a location but not a name. |
| 4081 | +# |
| 4082 | +def register_image(self, name=None, description=None, image_location=None, |
| 4083 | + architecture=None, kernel_id=None, ramdisk_id=None, |
| 4084 | + root_device_name=None, block_device_map=None): |
| 4085 | + """ |
| 4086 | + Register an image. |
| 4087 | + |
| 4088 | + :type name: string |
| 4089 | + :param name: The name of the AMI. Valid only for EBS-based images. |
| 4090 | + |
| 4091 | + :type description: string |
| 4092 | + :param description: The description of the AMI. |
| 4093 | + |
| 4094 | + :type image_location: string |
| 4095 | + :param image_location: Full path to your AMI manifest in Amazon S3 storage. |
| 4096 | + Only used for S3-based AMI's. |
| 4097 | + |
| 4098 | + :type architecture: string |
| 4099 | + :param architecture: The architecture of the AMI. Valid choices are: |
| 4100 | + i386 | x86_64 |
| 4101 | + |
| 4102 | + :type kernel_id: string |
| 4103 | + :param kernel_id: The ID of the kernel with which to launch the instances |
| 4104 | + |
| 4105 | + :type root_device_name: string |
| 4106 | + :param root_device_name: The root device name (e.g. /dev/sdh) |
| 4107 | + |
| 4108 | + :type block_device_map: :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping` |
| 4109 | + :param block_device_map: A BlockDeviceMapping data structure |
| 4110 | + describing the EBS volumes associated |
| 4111 | + with the Image. |
| 4112 | + |
| 4113 | + :rtype: string |
| 4114 | + :return: The new image id |
| 4115 | + """ |
| 4116 | + params = {} |
| 4117 | + if name: |
| 4118 | + params['Name'] = name |
| 4119 | + if description: |
| 4120 | + params['Description'] = description |
| 4121 | + if architecture: |
| 4122 | + params['Architecture'] = architecture |
| 4123 | + if kernel_id: |
| 4124 | + params['KernelId'] = kernel_id |
| 4125 | + if ramdisk_id: |
| 4126 | + params['RamdiskId'] = ramdisk_id |
| 4127 | + if image_location: |
| 4128 | + params['ImageLocation'] = image_location |
| 4129 | + if root_device_name: |
| 4130 | + params['RootDeviceName'] = root_device_name |
| 4131 | + if block_device_map: |
| 4132 | + block_device_map.build_list_params(params) |
| 4133 | + rs = self.get_object('RegisterImage', params, ResultSet) |
| 4134 | + image_id = getattr(rs, 'imageId', None) |
| 4135 | + return image_id |
| 4136 | + |
| 4137 | +EC2Connection.register_image = register_image |
| 4138 | + |
| 4139 | +class LinuxImage: |
| 4140 | + |
| 4141 | + ALLOWED_FS_TYPES = ['ext2', 'ext3', 'xfs', 'jfs', 'reiserfs'] |
| 4142 | + BANNED_MOUNTS = [ |
| 4143 | + '/dev', |
| 4144 | + '/media', |
| 4145 | + '/mnt', |
| 4146 | + '/proc', |
| 4147 | + '/sys', |
| 4148 | + '/cdrom', |
| 4149 | + '/tmp', |
| 4150 | + ] |
| 4151 | + ESSENTIAL_DIRS = ['proc', 'tmp', 'dev', 'mnt', 'sys'] |
| 4152 | + ESSENTIAL_DEVS = [ |
| 4153 | + [os.path.join('dev', 'console'), 'c', '5', '1'], |
| 4154 | + [os.path.join('dev', 'full'), 'c', '1', '7'], |
| 4155 | + [os.path.join('dev', 'null'), 'c', '1', '3'], |
| 4156 | + [os.path.join('dev', 'zero'), 'c', '1', '5'], |
| 4157 | + [os.path.join('dev', 'tty'), 'c', '5', '0'], |
| 4158 | + [os.path.join('dev', 'tty0'), 'c', '4', '0'], |
| 4159 | + [os.path.join('dev', 'tty1'), 'c', '4', '1'], |
| 4160 | + [os.path.join('dev', 'tty2'), 'c', '4', '2'], |
| 4161 | + [os.path.join('dev', 'tty3'), 'c', '4', '3'], |
| 4162 | + [os.path.join('dev', 'tty4'), 'c', '4', '4'], |
| 4163 | + [os.path.join('dev', 'tty5'), 'c', '4', '5'], |
| 4164 | + [os.path.join('dev', 'xvc0'), 'c', '204', '191'], |
| 4165 | + ] |
| 4166 | + MAKEFS_CMD = 'mkfs.ext3' |
| 4167 | + NEW_FSTAB = \ |
| 4168 | + """ |
| 4169 | +/dev/sda1 / ext3 defaults 1 1 |
| 4170 | +/dev/sdb /mnt ext3 defaults 0 0 |
| 4171 | +none /dev/pts devpts gid=5,mode=620 0 0 |
| 4172 | +none /proc proc defaults 0 0 |
| 4173 | +none /sys sysfs defaults 0 0 |
| 4174 | + """ |
| 4175 | + |
| 4176 | + OLD_FSTAB = \ |
| 4177 | + """/dev/sda1 / ext3 defaults,errors=remount-ro 0 0 |
| 4178 | +/dev/sda2 /mnt ext3 defaults 0 0 |
| 4179 | +/dev/sda3 swap swap defaults 0 0 |
| 4180 | +proc /proc proc defaults 0 0 |
| 4181 | +devpts /dev/pts devpts gid=5,mode=620 0 0""" |
| 4182 | + |
| 4183 | + def __init__(self, debug=False): |
| 4184 | + self.debug = debug |
| 4185 | + |
| 4186 | + def create_image(self, size_in_MB, image_path): |
| 4187 | + dd_cmd = ['dd'] |
| 4188 | + dd_cmd.append('if=/dev/zero') |
| 4189 | + dd_cmd.append('of=%s' % image_path) |
| 4190 | + dd_cmd.append('count=1') |
| 4191 | + dd_cmd.append('bs=1M') |
| 4192 | + dd_cmd.append('seek=%s' % (size_in_MB - 1)) |
| 4193 | + if self.debug: |
| 4194 | + print 'Creating disk image...', image_path |
| 4195 | + Popen(dd_cmd, PIPE).communicate()[0] |
| 4196 | + |
| 4197 | + def make_fs(self, image_path): |
| 4198 | + Util().check_prerequisite_command(self.MAKEFS_CMD) |
| 4199 | + |
| 4200 | + if self.debug: |
| 4201 | + print 'Creating filesystem...' |
| 4202 | + makefs_cmd = Popen([self.MAKEFS_CMD, '-F', image_path], |
| 4203 | + PIPE).communicate()[0] |
| 4204 | + |
| 4205 | + def add_fstab( |
| 4206 | + self, |
| 4207 | + mount_point, |
| 4208 | + generate_fstab, |
| 4209 | + fstab_path, |
| 4210 | + ): |
| 4211 | + if not fstab_path: |
| 4212 | + return |
| 4213 | + fstab = None |
| 4214 | + if fstab_path == 'old': |
| 4215 | + if not generate_fstab: |
| 4216 | + return |
| 4217 | + fstab = self.OLD_FSTAB |
| 4218 | + elif fstab_path == 'new': |
| 4219 | + if not generate_fstab: |
| 4220 | + return |
| 4221 | + fstab = self.NEW_FSTAB |
| 4222 | + |
| 4223 | + etc_file_path = os.path.join(mount_point, 'etc') |
| 4224 | + fstab_file_path = os.path.join(etc_file_path, 'fstab') |
| 4225 | + if not os.path.exists(etc_file_path): |
| 4226 | + os.mkdir(etc_file_path) |
| 4227 | + else: |
| 4228 | + if os.path.exists(fstab_file_path): |
| 4229 | + fstab_copy_path = fstab_file_path + '.old' |
| 4230 | + shutil.copyfile(fstab_file_path, fstab_copy_path) |
| 4231 | + |
| 4232 | + if self.debug: |
| 4233 | + print 'Updating fstab entry' |
| 4234 | + fstab_file = open(fstab_file_path, 'w') |
| 4235 | + if fstab: |
| 4236 | + fstab_file.write(fstab) |
| 4237 | + else: |
| 4238 | + orig_fstab_file = open(fstab_path, 'r') |
| 4239 | + while 1: |
| 4240 | + data = orig_fstab_file.read(IMAGE_IO_CHUNK) |
| 4241 | + if not data: |
| 4242 | + break |
| 4243 | + fstab_file.write(data) |
| 4244 | + orig_fstab_file.close() |
| 4245 | + fstab_file.close() |
| 4246 | + |
| 4247 | + def make_essential_devs(self, image_path): |
| 4248 | + for entry in self.ESSENTIAL_DEVS: |
| 4249 | + cmd = ['mknod'] |
| 4250 | + entry[0] = os.path.join(image_path, entry[0]) |
| 4251 | + cmd.extend(entry) |
| 4252 | + Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
| 4253 | + |
| 4254 | + |
| 4255 | +class SolarisImage: |
| 4256 | + |
| 4257 | + ALLOWED_FS_TYPES = ['ext2', 'ext3', 'xfs', 'jfs', 'reiserfs'] |
| 4258 | + BANNED_MOUNTS = [ |
| 4259 | + '/dev', |
| 4260 | + '/media', |
| 4261 | + '/mnt', |
| 4262 | + '/proc', |
| 4263 | + '/sys', |
| 4264 | + '/cdrom', |
| 4265 | + '/tmp', |
| 4266 | + ] |
| 4267 | + ESSENTIAL_DIRS = ['proc', 'tmp', 'dev', 'mnt', 'sys'] |
| 4268 | + |
| 4269 | + def __init__(self, debug=False): |
| 4270 | + self.debug = debug |
| 4271 | + |
| 4272 | + def create_image(self, size_in_MB, image_path): |
| 4273 | + print 'Sorry. Solaris not supported yet' |
| 4274 | + raise UnsupportedException |
| 4275 | + |
| 4276 | + def make_fs(self, image_path): |
| 4277 | + print 'Sorry. Solaris not supported yet' |
| 4278 | + raise UnsupportedException |
| 4279 | + |
| 4280 | + def make_essential_devs(self, image_path): |
| 4281 | + print 'Sorry. Solaris not supported yet' |
| 4282 | + raise UnsupportedException |
| 4283 | + |
| 4284 | + |
| 4285 | +class Util: |
| 4286 | + |
| 4287 | + usage_string = \ |
| 4288 | + """ |
| 4289 | +-a, --access-key User's Access Key ID. |
| 4290 | + |
| 4291 | +-s, --secret-key User's Secret Key. |
| 4292 | + |
| 4293 | +-U, --url URL of the Cloud to connect to. |
| 4294 | + |
| 4295 | +--config Read credentials and cloud settings from the |
| 4296 | + specified config file (defaults to $HOME/.eucarc or /etc/euca2ools/eucarc). |
| 4297 | + |
| 4298 | +-h, --help Display this help message. |
| 4299 | + |
| 4300 | +--version Display the version of this tool. |
| 4301 | + |
| 4302 | +--debug Turn on debugging. |
| 4303 | + |
| 4304 | +Euca2ools will use the environment variables EC2_URL, EC2_ACCESS_KEY, EC2_SECRET_KEY, EC2_CERT, EC2_PRIVATE_KEY, S3_URL, EUCALYPTUS_CERT by default. |
| 4305 | + """ |
| 4306 | + |
| 4307 | + version_string = """ Version: 1.2 (BSD)""" |
| 4308 | + |
| 4309 | + def version(self): |
| 4310 | + return self.version_string |
| 4311 | + |
| 4312 | + def usage(self, compat=False): |
| 4313 | + if compat: |
| 4314 | + self.usage_string = self.usage_string.replace('-s,', '-S,') |
| 4315 | + self.usage_string = self.usage_string.replace('-a,', '-A,') |
| 4316 | + print self.usage_string |
| 4317 | + |
| 4318 | + def check_prerequisite_command(self, command): |
| 4319 | + cmd = [command] |
| 4320 | + try: |
| 4321 | + output = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
| 4322 | + except OSError, e: |
| 4323 | + error_string = '%s' % e |
| 4324 | + if 'No such' in error_string: |
| 4325 | + print 'Command %s not found. Is it installed?' % command |
| 4326 | + raise NotFoundError |
| 4327 | + else: |
| 4328 | + raise OSError(e) |
| 4329 | + |
| 4330 | + |
| 4331 | +class AddressValidationError: |
| 4332 | + |
| 4333 | + def __init__(self): |
| 4334 | + self.message = 'Invalid address' |
| 4335 | + |
| 4336 | + |
| 4337 | +class InstanceValidationError: |
| 4338 | + |
| 4339 | + def __init__(self): |
| 4340 | + self.message = 'Invalid instance id' |
| 4341 | + |
| 4342 | + |
| 4343 | +class VolumeValidationError: |
| 4344 | + |
| 4345 | + def __init__(self): |
| 4346 | + self.message = 'Invalid volume id' |
| 4347 | + |
| 4348 | + |
| 4349 | +class SizeValidationError: |
| 4350 | + |
| 4351 | + def __init__(self): |
| 4352 | + self.message = 'Invalid size' |
| 4353 | + |
| 4354 | + |
| 4355 | +class SnapshotValidationError: |
| 4356 | + |
| 4357 | + def __init__(self): |
| 4358 | + self.message = 'Invalid snapshot id' |
| 4359 | + |
| 4360 | + |
| 4361 | +class ProtocolValidationError: |
| 4362 | + |
| 4363 | + def __init__(self): |
| 4364 | + self.message = 'Invalid protocol' |
| 4365 | + |
| 4366 | + |
| 4367 | +class FileValidationError: |
| 4368 | + |
| 4369 | + def __init__(self): |
| 4370 | + self.message = 'Invalid file' |
| 4371 | + |
| 4372 | + |
| 4373 | +class DirValidationError: |
| 4374 | + |
| 4375 | + def __init__(self): |
| 4376 | + self.message = 'Invalid directory' |
| 4377 | + |
| 4378 | + |
| 4379 | +class BundleValidationError: |
| 4380 | + |
| 4381 | + def __init__(self): |
| 4382 | + self.message = 'Invalid bundle id' |
| 4383 | + |
| 4384 | + |
| 4385 | +class CopyError: |
| 4386 | + |
| 4387 | + def __init__(self): |
| 4388 | + self.message = 'Unable to copy' |
| 4389 | + |
| 4390 | + |
| 4391 | +class MetadataReadError: |
| 4392 | + |
| 4393 | + def __init__(self): |
| 4394 | + self.message = 'Unable to read metadata' |
| 4395 | + |
| 4396 | + |
| 4397 | +class NullHandler(logging.Handler): |
| 4398 | + |
| 4399 | + def emit(self, record): |
| 4400 | + pass |
| 4401 | + |
| 4402 | + |
| 4403 | +class NotFoundError: |
| 4404 | + |
| 4405 | + def __init__(self): |
| 4406 | + self.message = 'Unable to find' |
| 4407 | + |
| 4408 | + |
| 4409 | +class UnsupportedException: |
| 4410 | + |
| 4411 | + def __init__(self): |
| 4412 | + self.message = 'Not supported' |
| 4413 | + |
| 4414 | + |
| 4415 | +class CommandFailed: |
| 4416 | + |
| 4417 | + def __init__(self): |
| 4418 | + self.message = 'Command failed' |
| 4419 | + |
| 4420 | + |
| 4421 | +class ConnectionFailed: |
| 4422 | + |
| 4423 | + def __init__(self): |
| 4424 | + self.message = 'Connection failed' |
| 4425 | + |
| 4426 | + |
| 4427 | +class ParseError: |
| 4428 | + |
| 4429 | + def __init__(self, msg): |
| 4430 | + self.message = msg |
| 4431 | + |
| 4432 | + |
| 4433 | +class Euca2ool: |
| 4434 | + |
| 4435 | + def process_args(self): |
| 4436 | + ids = [] |
| 4437 | + for arg in self.args: |
| 4438 | + ids.append(arg) |
| 4439 | + return ids |
| 4440 | + |
| 4441 | + def __init__( |
| 4442 | + self, |
| 4443 | + short_opts=None, |
| 4444 | + long_opts=None, |
| 4445 | + is_s3=False, |
| 4446 | + compat=False, |
| 4447 | + ): |
| 4448 | + self.ec2_user_access_key = None |
| 4449 | + self.ec2_user_secret_key = None |
| 4450 | + self.ec2_url = None |
| 4451 | + self.s3_url = None |
| 4452 | + self.config_file_path = None |
| 4453 | + self.is_s3 = is_s3 |
| 4454 | + if compat: |
| 4455 | + self.secret_key_opt = 'S' |
| 4456 | + self.access_key_opt = 'A' |
| 4457 | + else: |
| 4458 | + self.secret_key_opt = 's' |
| 4459 | + self.access_key_opt = 'a' |
| 4460 | + if not short_opts: |
| 4461 | + short_opts = '' |
| 4462 | + if not long_opts: |
| 4463 | + long_opts = [''] |
| 4464 | + short_opts += 'hU:' |
| 4465 | + short_opts += '%s:' % self.secret_key_opt |
| 4466 | + short_opts += '%s:' % self.access_key_opt |
| 4467 | + long_opts += [ |
| 4468 | + 'access-key=', |
| 4469 | + 'secret-key=', |
| 4470 | + 'url=', |
| 4471 | + 'help', |
| 4472 | + 'version', |
| 4473 | + 'debug', |
| 4474 | + 'config=', |
| 4475 | + ] |
| 4476 | + (opts, args) = getopt.gnu_getopt(sys.argv[1:], short_opts, |
| 4477 | + long_opts) |
| 4478 | + self.opts = opts |
| 4479 | + self.args = args |
| 4480 | + self.debug = False |
| 4481 | + for (name, value) in opts: |
| 4482 | + if name in ('-%s' % self.access_key_opt, '--access-key'): |
| 4483 | + self.ec2_user_access_key = value |
| 4484 | + elif name in ('-%s' % self.secret_key_opt, '--secret-key'): |
| 4485 | + try: |
| 4486 | + self.ec2_user_secret_key = int(value) |
| 4487 | + self.ec2_user_secret_key = None |
| 4488 | + except ValueError: |
| 4489 | + self.ec2_user_secret_key = value |
| 4490 | + elif name in ('-U', '--url'): |
| 4491 | + self.ec2_url = value |
| 4492 | + elif name == '--debug': |
| 4493 | + self.debug = True |
| 4494 | + elif name == '--config': |
| 4495 | + self.config_file_path = value |
| 4496 | + system_string = platform.system() |
| 4497 | + if system_string == 'Linux': |
| 4498 | + self.img = LinuxImage(self.debug) |
| 4499 | + elif system_string == 'SunOS': |
| 4500 | + self.img = SolarisImage(self.debug) |
| 4501 | + else: |
| 4502 | + self.img = 'Unsupported' |
| 4503 | + self.setup_environ() |
| 4504 | + |
| 4505 | + h = NullHandler() |
| 4506 | + logging.getLogger('boto').addHandler(h) |
| 4507 | + |
| 4508 | + SYSTEM_EUCARC_PATH = os.path.join('/etc', 'euca2ools', 'eucarc') |
| 4509 | + |
| 4510 | + def setup_environ(self): |
| 4511 | + envlist = ( |
| 4512 | + 'EC2_ACCESS_KEY', |
| 4513 | + 'EC2_SECRET_KEY', |
| 4514 | + 'S3_URL', |
| 4515 | + 'EC2_URL', |
| 4516 | + 'EC2_CERT', |
| 4517 | + 'EC2_PRIVATE_KEY', |
| 4518 | + 'EUCALYPTUS_CERT', |
| 4519 | + 'EC2_USER_ID', |
| 4520 | + ) |
| 4521 | + self.environ = {} |
| 4522 | + user_eucarc = None |
| 4523 | + if 'HOME' in os.environ: |
| 4524 | + user_eucarc = os.path.join(os.getenv('HOME'), '.eucarc') |
| 4525 | + read_config = False |
| 4526 | + if self.config_file_path \ |
| 4527 | + and os.path.exists(self.config_file_path): |
| 4528 | + read_config = self.config_file_path |
| 4529 | + elif user_eucarc is not None and os.path.exists(user_eucarc): |
| 4530 | + read_config = user_eucarc |
| 4531 | + elif os.path.exists(self.SYSTEM_EUCARC_PATH): |
| 4532 | + read_config = self.SYSTEM_EUCARC_PATH |
| 4533 | + if read_config: |
| 4534 | + parse_config(read_config, self.environ, envlist) |
| 4535 | + else: |
| 4536 | + for v in envlist: |
| 4537 | + self.environ[v] = os.getenv(v) |
| 4538 | + |
| 4539 | + def get_environ(self, name): |
| 4540 | + if self.environ.has_key(name): |
| 4541 | + return self.environ[name] |
| 4542 | + else: |
| 4543 | + print '%s not found' % name |
| 4544 | + raise NotFoundError |
| 4545 | + |
| 4546 | + def make_connection(self): |
| 4547 | + if not self.ec2_user_access_key: |
| 4548 | + self.ec2_user_access_key = self.environ['EC2_ACCESS_KEY'] |
| 4549 | + if not self.ec2_user_access_key: |
| 4550 | + print 'EC2_ACCESS_KEY environment variable must be set.' |
| 4551 | + raise ConnectionFailed |
| 4552 | + |
| 4553 | + if not self.ec2_user_secret_key: |
| 4554 | + self.ec2_user_secret_key = self.environ['EC2_SECRET_KEY'] |
| 4555 | + if not self.ec2_user_secret_key: |
| 4556 | + print 'EC2_SECRET_KEY environment variable must be set.' |
| 4557 | + raise ConnectionFailed |
| 4558 | + |
| 4559 | + if not self.is_s3: |
| 4560 | + if not self.ec2_url: |
| 4561 | + self.ec2_url = self.environ['EC2_URL'] |
| 4562 | + if not self.ec2_url: |
| 4563 | + self.ec2_url = \ |
| 4564 | + 'http://localhost:8773/services/Eucalyptus' |
| 4565 | + print 'EC2_URL not specified. Trying %s' \ |
| 4566 | + % self.ec2_url |
| 4567 | + else: |
| 4568 | + if not self.ec2_url: |
| 4569 | + self.ec2_url = self.environ['S3_URL'] |
| 4570 | + if not self.ec2_url: |
| 4571 | + self.ec2_url = \ |
| 4572 | + 'http://localhost:8773/services/Walrus' |
| 4573 | + print 'S3_URL not specified. Trying %s' \ |
| 4574 | + % self.ec2_url |
| 4575 | + |
| 4576 | + self.port = None |
| 4577 | + self.service_path = '/' |
| 4578 | + if self.ec2_url.find('https://') >= 0: |
| 4579 | + self.ec2_url = self.ec2_url.replace('https://', '') |
| 4580 | + self.is_secure = True |
| 4581 | + else: |
| 4582 | + self.ec2_url = self.ec2_url.replace('http://', '') |
| 4583 | + self.is_secure = False |
| 4584 | + self.host = self.ec2_url |
| 4585 | + url_parts = self.ec2_url.split(':') |
| 4586 | + if len(url_parts) > 1: |
| 4587 | + self.host = url_parts[0] |
| 4588 | + path_parts = url_parts[1].split('/', 1) |
| 4589 | + if len(path_parts) > 1: |
| 4590 | + self.port = int(path_parts[0]) |
| 4591 | + self.service_path = self.service_path + path_parts[1] |
| 4592 | + else: |
| 4593 | + self.port = int(url_parts[1]) |
| 4594 | + |
| 4595 | + if not self.is_s3: |
| 4596 | + return EC2Connection( |
| 4597 | + aws_access_key_id=self.ec2_user_access_key, |
| 4598 | + aws_secret_access_key=self.ec2_user_secret_key, |
| 4599 | + is_secure=self.is_secure, |
| 4600 | + region=RegionInfo(None, 'eucalyptus', self.host), |
| 4601 | + port=self.port, |
| 4602 | + path=self.service_path, |
| 4603 | + ) |
| 4604 | + else: |
| 4605 | + return boto.s3.Connection( |
| 4606 | + aws_access_key_id=self.ec2_user_access_key, |
| 4607 | + aws_secret_access_key=self.ec2_user_secret_key, |
| 4608 | + is_secure=self.is_secure, |
| 4609 | + host=self.host, |
| 4610 | + port=self.port, |
| 4611 | + calling_format=boto.s3.connection.OrdinaryCallingFormat(), |
| 4612 | + path=self.service_path, |
| 4613 | + ) |
| 4614 | + |
| 4615 | + def validate_address(self, address): |
| 4616 | + if not re.match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(\/[0-9]+)?$", |
| 4617 | + address): |
| 4618 | + raise AddressValidationError |
| 4619 | + |
| 4620 | + def validate_instance_id(self, id): |
| 4621 | + if not re.match('i-', id): |
| 4622 | + raise InstanceValidationError |
| 4623 | + |
| 4624 | + def validate_volume_id(self, id): |
| 4625 | + if not re.match('vol-', id): |
| 4626 | + raise VolumeValidationError |
| 4627 | + |
| 4628 | + def validate_volume_size(self, size): |
| 4629 | + if size < 0 or size > 1024: |
| 4630 | + raise SizeValidationError |
| 4631 | + |
| 4632 | + def validate_snapshot_id(self, id): |
| 4633 | + if not re.match('snap-', id): |
| 4634 | + raise SnapshotValidationError |
| 4635 | + |
| 4636 | + def validate_protocol(self, proto): |
| 4637 | + if not proto in IP_PROTOCOLS: |
| 4638 | + raise ProtocolValidationError |
| 4639 | + |
| 4640 | + def validate_file(self, path): |
| 4641 | + if not os.path.exists(path) or not os.path.isfile(path): |
| 4642 | + raise FileValidationError |
| 4643 | + |
| 4644 | + def validate_dir(self, path): |
| 4645 | + if not os.path.exists(path) or not os.path.isdir(path): |
| 4646 | + raise DirValidationError |
| 4647 | + |
| 4648 | + def validate_bundle_id(self, id): |
| 4649 | + if not re.match('bun-', id): |
| 4650 | + raise BundleValidationError |
| 4651 | + |
| 4652 | + def get_relative_filename(self, filename): |
| 4653 | + f_parts = filename.split('/') |
| 4654 | + return f_parts[len(f_parts) - 1] |
| 4655 | + |
| 4656 | + def get_file_path(self, filename): |
| 4657 | + relative_filename = self.get_relative_filename(filename) |
| 4658 | + file_path = os.path.dirname(filename) |
| 4659 | + if len(file_path) == 0: |
| 4660 | + file_path = '.' |
| 4661 | + return file_path |
| 4662 | + |
| 4663 | + def split_file(self, file, chunk_size): |
| 4664 | + parts = [] |
| 4665 | + parts_digest = [] |
| 4666 | + file_size = os.path.getsize(file) |
| 4667 | + in_file = open(file, 'rb') |
| 4668 | + number_parts = int(file_size / chunk_size) |
| 4669 | + number_parts += 1 |
| 4670 | + bytes_read = 0 |
| 4671 | + for i in range(0, number_parts, 1): |
| 4672 | + filename = '%s.%d' % (file, i) |
| 4673 | + part_digest = sha() |
| 4674 | + file_part = open(filename, 'wb') |
| 4675 | + print 'Part:', self.get_relative_filename(filename) |
| 4676 | + part_bytes_written = 0 |
| 4677 | + while part_bytes_written < IMAGE_SPLIT_CHUNK: |
| 4678 | + data = in_file.read(IMAGE_IO_CHUNK) |
| 4679 | + file_part.write(data) |
| 4680 | + part_digest.update(data) |
| 4681 | + data_len = len(data) |
| 4682 | + part_bytes_written += data_len |
| 4683 | + bytes_read += data_len |
| 4684 | + if bytes_read >= file_size: |
| 4685 | + break |
| 4686 | + file_part.close() |
| 4687 | + parts.append(filename) |
| 4688 | + parts_digest.append(hexlify(part_digest.digest())) |
| 4689 | + |
| 4690 | + in_file.close() |
| 4691 | + return (parts, parts_digest) |
| 4692 | + |
| 4693 | + def check_image(self, image_file, path): |
| 4694 | + print 'Checking image' |
| 4695 | + if not os.path.exists(path): |
| 4696 | + os.makedirs(path) |
| 4697 | + image_size = os.path.getsize(image_file) |
| 4698 | + if self.debug: |
| 4699 | + print 'Image Size:', image_size, 'bytes' |
| 4700 | + in_file = open(image_file, 'rb') |
| 4701 | + sha_image = sha() |
| 4702 | + while 1: |
| 4703 | + buf = in_file.read(IMAGE_IO_CHUNK) |
| 4704 | + if not buf: |
| 4705 | + break |
| 4706 | + sha_image.update(buf) |
| 4707 | + return (image_size, hexlify(sha_image.digest())) |
| 4708 | + |
| 4709 | + def tarzip_image( |
| 4710 | + self, |
| 4711 | + prefix, |
| 4712 | + file, |
| 4713 | + path, |
| 4714 | + ): |
| 4715 | + Util().check_prerequisite_command('tar') |
| 4716 | + |
| 4717 | + print 'Tarring image' |
| 4718 | + tar_file = '%s.tar.gz' % os.path.join(path, prefix) |
| 4719 | + outfile = open(tar_file, 'wb') |
| 4720 | + file_path = self.get_file_path(file) |
| 4721 | + tar_cmd = ['tar', 'ch', '-S'] |
| 4722 | + if file_path: |
| 4723 | + tar_cmd.append('-C') |
| 4724 | + tar_cmd.append(file_path) |
| 4725 | + tar_cmd.append(self.get_relative_filename(file)) |
| 4726 | + else: |
| 4727 | + tar_cmd.append(file) |
| 4728 | + p1 = Popen(tar_cmd, stdout=PIPE) |
| 4729 | + p2 = Popen(['gzip'], stdin=p1.stdout, stdout=outfile) |
| 4730 | + p2.communicate() |
| 4731 | + outfile.close |
| 4732 | + if os.path.getsize(tar_file) <= 0: |
| 4733 | + print 'Could not tar image' |
| 4734 | + raise CommandFailed |
| 4735 | + return tar_file |
| 4736 | + |
| 4737 | + def hexToBytes(self, hexString): |
| 4738 | + bytes = [] |
| 4739 | + hexString = ''.join(hexString.split(' ')) |
| 4740 | + for i in range(0, len(hexString), 2): |
| 4741 | + bytes.append(chr(int(hexString[i:i + 2], 16))) |
| 4742 | + |
| 4743 | + return ''.join(bytes) |
| 4744 | + |
| 4745 | + def crypt_file( |
| 4746 | + self, |
| 4747 | + cipher, |
| 4748 | + in_file, |
| 4749 | + out_file, |
| 4750 | + ): |
| 4751 | + while 1: |
| 4752 | + buf = in_file.read(IMAGE_IO_CHUNK) |
| 4753 | + if not buf: |
| 4754 | + break |
| 4755 | + out_file.write(cipher.update(buf)) |
| 4756 | + out_file.write(cipher.final()) |
| 4757 | + |
| 4758 | + def encrypt_image(self, file): |
| 4759 | + print 'Encrypting image' |
| 4760 | + enc_file = '%s.part' % file.replace('.tar.gz', '') |
| 4761 | + |
| 4762 | + key = hex(BN.rand(16 * 8))[2:34].replace('L', 'c') |
| 4763 | + if self.debug: |
| 4764 | + print 'Key: %s' % key |
| 4765 | + iv = hex(BN.rand(16 * 8))[2:34].replace('L', 'c') |
| 4766 | + if self.debug: |
| 4767 | + print 'IV: %s' % iv |
| 4768 | + |
| 4769 | + k = EVP.Cipher(alg='aes_128_cbc', key=unhexlify(key), |
| 4770 | + iv=unhexlify(iv), op=1) |
| 4771 | + |
| 4772 | + in_file = open(file) |
| 4773 | + out_file = open(enc_file, 'wb') |
| 4774 | + self.crypt_file(k, in_file, out_file) |
| 4775 | + in_file.close() |
| 4776 | + out_file.close() |
| 4777 | + bundled_size = os.path.getsize(enc_file) |
| 4778 | + return (enc_file, key, iv, bundled_size) |
| 4779 | + |
| 4780 | + def split_image(self, file): |
| 4781 | + print 'Splitting image...' |
| 4782 | + return self.split_file(file, IMAGE_SPLIT_CHUNK) |
| 4783 | + |
| 4784 | + def get_verification_string(self, manifest_string): |
| 4785 | + start_mc = manifest_string.find('<machine_configuration>') |
| 4786 | + end_mc = manifest_string.find('</machine_configuration>') |
| 4787 | + mc_config_string = manifest_string[start_mc:end_mc |
| 4788 | + + len('</machine_configuration>')] |
| 4789 | + start_image = manifest_string.find('<image>') |
| 4790 | + end_image = manifest_string.find('</image>') |
| 4791 | + image_string = manifest_string[start_image:end_image |
| 4792 | + + len('</image>')] |
| 4793 | + |
| 4794 | + return mc_config_string + image_string |
| 4795 | + |
| 4796 | + def parse_manifest(self, manifest_filename): |
| 4797 | + parts = [] |
| 4798 | + encrypted_key = None |
| 4799 | + encrypted_iv = None |
| 4800 | + dom = minidom.parse(manifest_filename) |
| 4801 | + manifest_elem = dom.getElementsByTagName('manifest')[0] |
| 4802 | + parts_list = manifest_elem.getElementsByTagName('filename') |
| 4803 | + for part_elem in parts_list: |
| 4804 | + nodes = part_elem.childNodes |
| 4805 | + for node in nodes: |
| 4806 | + if node.nodeType == node.TEXT_NODE: |
| 4807 | + parts.append(node.data) |
| 4808 | + encrypted_key_elem = \ |
| 4809 | + manifest_elem.getElementsByTagName('user_encrypted_key')[0] |
| 4810 | + nodes = encrypted_key_elem.childNodes |
| 4811 | + for node in nodes: |
| 4812 | + if node.nodeType == node.TEXT_NODE: |
| 4813 | + encrypted_key = node.data |
| 4814 | + encrypted_iv_elem = \ |
| 4815 | + manifest_elem.getElementsByTagName('user_encrypted_iv')[0] |
| 4816 | + nodes = encrypted_iv_elem.childNodes |
| 4817 | + for node in nodes: |
| 4818 | + if node.nodeType == node.TEXT_NODE: |
| 4819 | + encrypted_iv = node.data |
| 4820 | + return (parts, encrypted_key, encrypted_iv) |
| 4821 | + |
| 4822 | + def assemble_parts( |
| 4823 | + self, |
| 4824 | + src_directory, |
| 4825 | + directory, |
| 4826 | + manifest_path, |
| 4827 | + parts, |
| 4828 | + ): |
| 4829 | + manifest_filename = self.get_relative_filename(manifest_path) |
| 4830 | + encrypted_filename = os.path.join(directory, |
| 4831 | + manifest_filename.replace('.manifest.xml', '.enc.tar.gz' |
| 4832 | + )) |
| 4833 | + if len(parts) > 0: |
| 4834 | + if not os.path.exists(directory): |
| 4835 | + os.makedirs(directory) |
| 4836 | + encrypted_file = open(encrypted_filename, 'wb') |
| 4837 | + for part in parts: |
| 4838 | + print 'Part:', self.get_relative_filename(part) |
| 4839 | + part_filename = os.path.join(src_directory, part) |
| 4840 | + part_file = open(part_filename, 'rb') |
| 4841 | + while 1: |
| 4842 | + data = part_file.read(IMAGE_IO_CHUNK) |
| 4843 | + if not data: |
| 4844 | + break |
| 4845 | + encrypted_file.write(data) |
| 4846 | + part_file.close() |
| 4847 | + encrypted_file.close() |
| 4848 | + return encrypted_filename |
| 4849 | + |
| 4850 | + def decrypt_image( |
| 4851 | + self, |
| 4852 | + encrypted_filename, |
| 4853 | + encrypted_key, |
| 4854 | + encrypted_iv, |
| 4855 | + private_key_path, |
| 4856 | + ): |
| 4857 | + user_priv_key = RSA.load_key(private_key_path) |
| 4858 | + key = user_priv_key.private_decrypt(unhexlify(encrypted_key), |
| 4859 | + RSA.pkcs1_padding) |
| 4860 | + iv = user_priv_key.private_decrypt(unhexlify(encrypted_iv), |
| 4861 | + RSA.pkcs1_padding) |
| 4862 | + k = EVP.Cipher(alg='aes_128_cbc', key=unhexlify(key), |
| 4863 | + iv=unhexlify(iv), op=0) |
| 4864 | + |
| 4865 | + decrypted_filename = encrypted_filename.replace('.enc', '') |
| 4866 | + decrypted_file = open(decrypted_filename, 'wb') |
| 4867 | + encrypted_file = open(encrypted_filename, 'rb') |
| 4868 | + self.crypt_file(k, encrypted_file, decrypted_file) |
| 4869 | + encrypted_file.close() |
| 4870 | + decrypted_file.close() |
| 4871 | + return decrypted_filename |
| 4872 | + |
| 4873 | + def decrypt_string( |
| 4874 | + self, |
| 4875 | + encrypted_string, |
| 4876 | + private_key_path, |
| 4877 | + encoded=False, |
| 4878 | + ): |
| 4879 | + user_priv_key = RSA.load_key(private_key_path) |
| 4880 | + string_to_decrypt = encrypted_string |
| 4881 | + if encoded: |
| 4882 | + string_to_decrypt = base64.b64decode(encrypted_string) |
| 4883 | + return user_priv_key.private_decrypt(string_to_decrypt, |
| 4884 | + RSA.pkcs1_padding) |
| 4885 | + |
| 4886 | + def untarzip_image(self, path, file): |
| 4887 | + untarred_filename = file.replace('.tar.gz', '') |
| 4888 | + tar_file = tarfile.open(file, 'r|gz') |
| 4889 | + tar_file.extractall(path) |
| 4890 | + untarred_names = tar_file.getnames() |
| 4891 | + tar_file.close() |
| 4892 | + return untarred_names |
| 4893 | + |
| 4894 | + def get_block_devs(self, mapping): |
| 4895 | + virtual = [] |
| 4896 | + devices = [] |
| 4897 | + |
| 4898 | + vname = None |
| 4899 | + for m in mapping: |
| 4900 | + if not vname: |
| 4901 | + vname = m |
| 4902 | + virtual.append(vname) |
| 4903 | + else: |
| 4904 | + devices.append(m) |
| 4905 | + vname = None |
| 4906 | + |
| 4907 | + return (virtual, devices) |
| 4908 | + |
| 4909 | + def generate_manifest( |
| 4910 | + self, |
| 4911 | + path, |
| 4912 | + prefix, |
| 4913 | + parts, |
| 4914 | + parts_digest, |
| 4915 | + file, |
| 4916 | + key, |
| 4917 | + iv, |
| 4918 | + cert_path, |
| 4919 | + ec2cert_path, |
| 4920 | + private_key_path, |
| 4921 | + target_arch, |
| 4922 | + image_size, |
| 4923 | + bundled_size, |
| 4924 | + image_digest, |
| 4925 | + user, |
| 4926 | + kernel, |
| 4927 | + ramdisk, |
| 4928 | + mapping=None, |
| 4929 | + product_codes=None, |
| 4930 | + ancestor_ami_ids=None, |
| 4931 | + ): |
| 4932 | + user_pub_key = X509.load_cert(cert_path).get_pubkey().get_rsa() |
| 4933 | + cloud_pub_key = \ |
| 4934 | + X509.load_cert(ec2cert_path).get_pubkey().get_rsa() |
| 4935 | + |
| 4936 | + user_encrypted_key = hexlify(user_pub_key.public_encrypt(key, |
| 4937 | + RSA.pkcs1_padding)) |
| 4938 | + user_encrypted_iv = hexlify(user_pub_key.public_encrypt(iv, |
| 4939 | + RSA.pkcs1_padding)) |
| 4940 | + |
| 4941 | + cloud_encrypted_key = hexlify(cloud_pub_key.public_encrypt(key, |
| 4942 | + RSA.pkcs1_padding)) |
| 4943 | + cloud_encrypted_iv = hexlify(cloud_pub_key.public_encrypt(iv, |
| 4944 | + RSA.pkcs1_padding)) |
| 4945 | + |
| 4946 | + user_priv_key = RSA.load_key(private_key_path) |
| 4947 | + |
| 4948 | + manifest_file = '%s.manifest.xml' % os.path.join(path, prefix) |
| 4949 | + if self.debug: |
| 4950 | + print 'Manifest: ', manifest_file |
| 4951 | + |
| 4952 | + print 'Generating manifest %s' % manifest_file |
| 4953 | + |
| 4954 | + manifest_out_file = open(manifest_file, 'wb') |
| 4955 | + doc = Document() |
| 4956 | + |
| 4957 | + manifest_elem = doc.createElement('manifest') |
| 4958 | + doc.appendChild(manifest_elem) |
| 4959 | + |
| 4960 | + # version |
| 4961 | + |
| 4962 | + version_elem = doc.createElement('version') |
| 4963 | + version_value = doc.createTextNode(VERSION) |
| 4964 | + version_elem.appendChild(version_value) |
| 4965 | + manifest_elem.appendChild(version_elem) |
| 4966 | + |
| 4967 | + # bundler info |
| 4968 | + |
| 4969 | + bundler_elem = doc.createElement('bundler') |
| 4970 | + bundler_name_elem = doc.createElement('name') |
| 4971 | + bundler_name_value = doc.createTextNode(BUNDLER_NAME) |
| 4972 | + bundler_name_elem.appendChild(bundler_name_value) |
| 4973 | + bundler_version_elem = doc.createElement('version') |
| 4974 | + bundler_version_value = doc.createTextNode(BUNDLER_VERSION) |
| 4975 | + bundler_version_elem.appendChild(bundler_version_value) |
| 4976 | + bundler_elem.appendChild(bundler_name_elem) |
| 4977 | + bundler_elem.appendChild(bundler_version_elem) |
| 4978 | + |
| 4979 | + # release |
| 4980 | + |
| 4981 | + release_elem = doc.createElement('release') |
| 4982 | + release_value = doc.createTextNode(RELEASE) |
| 4983 | + release_elem.appendChild(release_value) |
| 4984 | + bundler_elem.appendChild(release_elem) |
| 4985 | + manifest_elem.appendChild(bundler_elem) |
| 4986 | + |
| 4987 | + # machine config |
| 4988 | + |
| 4989 | + machine_config_elem = doc.createElement('machine_configuration') |
| 4990 | + manifest_elem.appendChild(machine_config_elem) |
| 4991 | + |
| 4992 | + target_arch_elem = doc.createElement('architecture') |
| 4993 | + target_arch_value = doc.createTextNode(target_arch) |
| 4994 | + target_arch_elem.appendChild(target_arch_value) |
| 4995 | + machine_config_elem.appendChild(target_arch_elem) |
| 4996 | + |
| 4997 | + # block device mapping |
| 4998 | + |
| 4999 | + if mapping: |
| 5000 | + block_dev_mapping_elem = \ |
The diff has been truncated for viewing.


Nice one Scott!
Have you had any success in pushing these patches upstream?
Are you able to get this uploaded ASAP... would be good to start hammering it..