summaryrefslogtreecommitdiff
path: root/legacy/gcimagebundle/gcimagebundlelib/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'legacy/gcimagebundle/gcimagebundlelib/utils.py')
-rw-r--r--legacy/gcimagebundle/gcimagebundlelib/utils.py455
1 files changed, 0 insertions, 455 deletions
diff --git a/legacy/gcimagebundle/gcimagebundlelib/utils.py b/legacy/gcimagebundle/gcimagebundlelib/utils.py
deleted file mode 100644
index a8fde40..0000000
--- a/legacy/gcimagebundle/gcimagebundlelib/utils.py
+++ /dev/null
@@ -1,455 +0,0 @@
-# Copyright 2013 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-"""Utilities for image bundling tool."""
-
-import logging
-import os
-import subprocess
-import time
-import urllib2
-
-METADATA_URL_PREFIX = 'http://169.254.169.254/computeMetadata/'
-METADATA_V1_URL_PREFIX = METADATA_URL_PREFIX + 'v1/'
-
-
-class MakeFileSystemException(Exception):
- """Error occurred in file system creation."""
-
-
-class TarAndGzipFileException(Exception):
- """Error occurred in creating the tarball."""
-
-
-class LoadDiskImage(object):
- """Loads raw disk image using kpartx."""
-
- def __init__(self, file_path):
- """Initializes LoadDiskImage object.
-
- Args:
- file_path: a path to a file containing raw disk image.
-
- Returns:
- A list of devices for every partition found in an image.
- """
- self._file_path = file_path
-
- def __enter__(self):
- """Map disk image as a device."""
- SyncFileSystem()
- kpartx_cmd = ['kpartx', '-a', '-v', '-s', self._file_path]
- output = RunCommand(kpartx_cmd)
- devs = []
- for line in output.splitlines():
- split_line = line.split()
- if (len(split_line) > 2 and split_line[0] == 'add'
- and split_line[1] == 'map'):
- devs.append('/dev/mapper/' + split_line[2])
- time.sleep(2)
- return devs
-
- def __exit__(self, unused_exc_type, unused_exc_value, unused_exc_tb):
- """Unmap disk image as a device.
-
- Args:
- unused_exc_type: unused.
- unused_exc_value: unused.
- unused_exc_tb: unused.
- """
- SyncFileSystem()
- time.sleep(2)
- kpartx_cmd = ['kpartx', '-d', '-v', '-s', self._file_path]
- RunCommand(kpartx_cmd)
-
-
-class MountFileSystem(object):
- """Mounts a file system."""
-
- def __init__(self, dev_path, dir_path, fs_type):
- """Initializes MountFileSystem object.
-
- Args:
- dev_path: A path to a device to mount.
- dir_path: A path to a directory where a device is to be mounted.
- """
- self._dev_path = dev_path
- self._dir_path = dir_path
- self._fs_type = fs_type
-
- def __enter__(self):
- """Mounts a device.
- """
- # Since the bundled image can have the same uuid as the root disk,
- # we should prevent uuid conflicts for xfs mounts.
- if self._fs_type is 'xfs':
- mount_cmd = ['mount', '-o', 'nouuid', self._dev_path, self._dir_path]
- else:
- mount_cmd = ['mount', self._dev_path, self._dir_path]
- RunCommand(mount_cmd)
-
- def __exit__(self, unused_exc_type, unused_exc_value, unused_exc_tb):
- """Unmounts a file system.
-
- Args:
- unused_exc_type: unused.
- unused_exc_value: unused.
- unused_exc_tb: unused.
- """
- umount_cmd = ['umount', self._dir_path]
- RunCommand(umount_cmd)
- SyncFileSystem()
-
-
-def SyncFileSystem():
- RunCommand(['sync'])
-
-def GetMounts(root='/'):
- """Find all mount points under the specified root.
-
- Args:
- root: a path to look for a mount points.
-
- Returns:
- A list of mount points.
- """
- output = RunCommand(['/bin/mount', '-l'])
- mounts = []
- for line in output.splitlines():
- split_line = line.split()
- mount_point = split_line[2]
- if mount_point == root:
- continue
- # We are simply ignoring the fs_type of fs for now. But we can use that
- # later Just verify that these are actually mount points.
- if os.path.ismount(mount_point) and mount_point.startswith(root):
- mounts.append(mount_point)
- return mounts
-
-
-def MakePartitionTable(file_path):
- """Create a partition table in a file.
-
- Args:
- file_path: A path to a file where a partition table will be created.
- """
- RunCommand(['parted', file_path, 'mklabel', 'msdos'])
-
-
-def MakePartition(file_path, partition_type, fs_type, start, end):
- """Create a partition in a file.
-
- Args:
- file_path: A path to a file where a partition will be created.
- partition_type: A type of a partition to be created. Tested option is msdos.
- fs_type: A type of a file system to be created. For example, ext2, ext3,
- etc.
- start: Start offset of a partition in bytes.
- end: End offset of a partition in bytes.
- """
- parted_cmd = ['parted', file_path, 'unit B', 'mkpart', partition_type,
- fs_type, str(start), str(end)]
- RunCommand(parted_cmd)
-
-
-def MakeFileSystem(dev_path, fs_type, uuid=None):
- """Create a file system in a device.
-
- Args:
- dev_path: A path to a device.
- fs_type: A type of a file system to be created. For example ext2, ext3, etc.
- uuid: The value to use as the UUID for the filesystem. If none, a random
- UUID will be generated and used.
-
- Returns:
- The uuid of the filesystem. This will be the same as the passed value if
- a value was specified. If no uuid was passed in, this will be the randomly
- generated uuid.
-
- Raises:
- MakeFileSystemException: If mkfs encounters an error.
- """
- if uuid is None:
- uuid = RunCommand(['uuidgen']).strip()
- if uuid is None:
- raise MakeFileSystemException(dev_path)
-
- mkfs_cmd = ['mkfs', '-t', fs_type, dev_path]
- RunCommand(mkfs_cmd)
-
- if fs_type is 'xfs':
- set_uuid_cmd = ['xfs_admin', '-U', uuid, dev_path]
- else:
- set_uuid_cmd = ['tune2fs', '-U', uuid, dev_path]
- RunCommand(set_uuid_cmd)
-
- return uuid
-
-
-def Rsync(src, dest, exclude_file, ignore_hard_links, recursive, xattrs):
- """Copy files from specified directory using rsync.
-
- Args:
- src: Source location to copy.
- dest: Destination to copy files to.
- exclude_file: A path to a file which contains a list of exclude from copy
- filters.
- ignore_hard_links: If True a hard links are copied as a separate files. If
- False, hard link are recreated in dest.
- recursive: Specifies if directories are copied recursively or not.
- xattrs: Specifies if extended attributes are preserved or not.
- """
- rsync_cmd = ['rsync', '--times', '--perms', '--owner', '--group', '--links',
- '--devices', '--acls', '--sparse']
- if not ignore_hard_links:
- rsync_cmd.append('--hard-links')
- if recursive:
- rsync_cmd.append('--recursive')
- else:
- rsync_cmd.append('--dirs')
- if xattrs:
- rsync_cmd.append('--xattrs')
- if exclude_file:
- rsync_cmd.append('--exclude-from=' + exclude_file)
- rsync_cmd.extend([src, dest])
-
- logging.debug('Calling: %s', repr(rsync_cmd))
- if exclude_file:
- logging.debug('Contents of exclude file %s:', exclude_file)
- with open(exclude_file, 'rb') as excludes:
- for line in excludes:
- logging.debug(' %s', line.rstrip())
-
- RunCommand(rsync_cmd)
-
-
-def GetUUID(partition_path):
- """Fetches the UUID of the filesystem on the specified partition.
-
- Args:
- partition_path: The path to the partition.
-
- Returns:
- The uuid of the filesystem.
- """
- output = RunCommand(['blkid', partition_path])
- for token in output.split():
- if token.startswith('UUID='):
- uuid = token.strip()[len('UUID="'):-1]
-
- logging.debug('found uuid = %s', uuid)
- return uuid
-
-
-def CopyBytes(src, dest, count):
- """Copies count bytes from the src to dest file.
-
- Args:
- src: The source to read bytes from.
- dest: The destination to copy bytes to.
- count: Number of bytes to copy.
- """
- block_size = 4096
- block_count = count / block_size
- dd_command = ['dd',
- 'if=%s' % src,
- 'of=%s' % dest,
- 'conv=notrunc',
- 'bs=%s' % block_size,
- 'count=%s' % block_count]
- RunCommand(dd_command)
- remaining_bytes = count - block_count * block_size
- if remaining_bytes:
- logging.debug('remaining bytes to copy = %s', remaining_bytes)
- dd_command = ['dd',
- 'if=%s' % src,
- 'of=%s' % dest,
- 'seek=%s' % block_count,
- 'skip=%s' % block_count,
- 'conv=notrunc',
- 'bs=1',
- 'count=%s' % remaining_bytes]
- RunCommand(dd_command)
-
-
-def GetPartitionStart(disk_path, partition_number):
- """Returns the starting position in bytes of the partition.
-
- Args:
- disk_path: The path to disk device.
- partition_number: The partition number to lookup. 1 based.
-
- Returns:
- The starting position of the first partition in bytes.
-
- Raises:
- subprocess.CalledProcessError: If running parted fails.
- IndexError: If there is no partition at the given number.
- """
- parted_cmd = ['parted',
- disk_path,
- 'unit B',
- 'print']
- # In case the device is not valid and parted throws the retry/cancel prompt
- # write c to stdin.
- output = RunCommand(parted_cmd, input_str='c')
- for line in output.splitlines():
- split_line = line.split()
- if len(split_line) > 4 and split_line[0] == str(partition_number):
- return int(split_line[1][:-1])
- raise IndexError()
-
-
-def RemovePartition(disk_path, partition_number):
- """Removes the partition number from the disk.
-
- Args:
- disk_path: The disk to remove the partition from.
- partition_number: The partition number to remove.
- """
- parted_cmd = ['parted',
- disk_path,
- 'rm',
- str(partition_number)]
- # In case the device is not valid and parted throws the retry/cancel prompt
- # write c to stdin.
- RunCommand(parted_cmd, input_str='c')
-
-
-def GetDiskSize(disk_file):
- """Returns the size of the disk device in bytes.
-
- Args:
- disk_file: The full path to the disk device.
-
- Returns:
- The size of the disk device in bytes.
-
- Raises:
- subprocess.CalledProcessError: If fdisk command fails for the disk file.
- """
- output = RunCommand(['fdisk', '-s', disk_file])
- return int(output) * 1024
-
-
-def RunCommand(command, input_str=None):
- """Runs the command and returns the output printed on stdout.
-
- Args:
- command: The command to run.
- input_str: The input to pass to subprocess via stdin.
-
- Returns:
- The stdout from running the command.
-
- Raises:
- subprocess.CalledProcessError: if the command fails.
- """
- logging.debug('running %s with input=%s', command, input_str)
- p = subprocess.Popen(command, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- cmd_output = p.communicate(input_str)
- logging.debug('stdout %s', cmd_output[0])
- logging.debug('stderr %s', cmd_output[1])
- logging.debug('returncode %s', p.returncode)
- if p.returncode:
- logging.warning('Error while running %s return_code = %s\n'
- 'stdout=%s\nstderr=%s',
- command, p.returncode, cmd_output[0],
- cmd_output[1])
- raise subprocess.CalledProcessError(p.returncode,
- cmd=command)
- return cmd_output[0]
-
-
-def TarAndGzipFile(src_paths, dest):
- """Pack file in tar archive and optionally gzip it.
-
- Args:
- src_paths: A list of files that will be archived.
- (Must be in the same directory.)
- dest: An archive name. If a file ends with .gz or .tgz an archive is gzipped
- as well.
-
- Raises:
- TarAndGzipFileException: If tar encounters an error.
- """
- if dest.endswith('.gz') or dest.endswith('.tgz'):
- mode = 'czSf'
- else:
- mode = 'cSf'
- src_names = [os.path.basename(src_path) for src_path in src_paths]
- # Take the directory of the first file in the list, all files are expected
- # to be in the same directory.
- src_dir = os.path.dirname(src_paths[0])
- tar_cmd = ['tar', mode, dest, '-C', src_dir] + src_names
- retcode = subprocess.call(tar_cmd)
- if retcode:
- raise TarAndGzipFileException(','.join(src_paths))
-
-
-class Http(object):
- def Get(self, request, timeout=None):
- return urllib2.urlopen(request, timeout=timeout).read()
-
- def GetMetadata(self, url_path, recursive=False, timeout=None):
- """Retrieves instance metadata.
-
- Args:
- url_path: The path of the metadata url after the api version.
- http://169.254.169.254/computeMetadata/v1/url_path
- recursive: If set, returns the tree of metadata starting at url_path as
- a json string.
- timeout: How long to wait for blocking operations (in seconds).
- A value of None uses urllib2's default timeout.
- Returns:
- The metadata returned based on the url path.
-
- """
- # Use the latest version of the metadata.
- suffix = ''
- if recursive:
- suffix = '?recursive=true'
- url = '{0}{1}{2}'.format(METADATA_V1_URL_PREFIX, url_path, suffix)
- request = urllib2.Request(url)
- request.add_unredirected_header('Metadata-Flavor', 'Google')
- return self.Get(request, timeout=timeout)
-
-
-def IsRunningOnGCE():
- """Detect if we are running on GCE.
-
- Returns:
- True if we are running on GCE, False otherwise.
- """
- # Try accessing DMI/SMBIOS informations through dmidecode first
- try:
- dmidecode_cmd = ['dmidecode', '-s', 'bios-vendor']
- output = RunCommand(dmidecode_cmd)
- return 'Google' in output
- except subprocess.CalledProcessError:
- # We fail if dmidecode doesn't exist or we have insufficient privileges
- pass
-
- # If dmidecode is not working, fallback to contacting the metadata server
- try:
- Http().GetMetadata('instance/id', timeout=1)
- return True
- except urllib2.HTTPError as e:
- logging.warning('HTTP error: %s (http status code=%s)' % (e.reason, e.code))
- except urllib2.URLError as e:
- logging.warning('Cannot reach metadata server: %s' % e.reason)
-
- return False