mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-08-24 15:36:27 +00:00
Merge pull request #80 from rybaktomasz/gce_development
Add Google Cloud Engine (GCE) provider:
This commit is contained in:
commit
c4e19986dc
12 changed files with 413 additions and 3 deletions
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
2014-05-02:
|
||||||
|
Tomasz Rybak:
|
||||||
|
* Added Google Compute Engine Provider
|
||||||
|
|
|
@ -78,7 +78,7 @@ class AbstractPartitionMap(FSMProxy):
|
||||||
'(?P<start_blk>\d) (?P<num_blks>\d+) '
|
'(?P<start_blk>\d) (?P<num_blks>\d+) '
|
||||||
'{device_path} (?P<blk_offset>\d+)$'
|
'{device_path} (?P<blk_offset>\d+)$'
|
||||||
.format(device_path=volume.device_path))
|
.format(device_path=volume.device_path))
|
||||||
log_check_call(['kpartx', '-a', volume.device_path])
|
log_check_call(['kpartx', '-as', volume.device_path])
|
||||||
import os.path
|
import os.path
|
||||||
# Run through the kpartx output and map the paths to the partitions
|
# Run through the kpartx output and map the paths to the partitions
|
||||||
for mapping in mappings:
|
for mapping in mappings:
|
||||||
|
@ -99,7 +99,7 @@ class AbstractPartitionMap(FSMProxy):
|
||||||
for partition in self.partitions:
|
for partition in self.partitions:
|
||||||
if not partition.fsm.can('unmap'):
|
if not partition.fsm.can('unmap'):
|
||||||
partition.unmap()
|
partition.unmap()
|
||||||
log_check_call(['kpartx', '-d', volume.device_path])
|
log_check_call(['kpartx', '-ds', volume.device_path])
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def unmap(self, volume):
|
def unmap(self, volume):
|
||||||
|
@ -122,7 +122,7 @@ class AbstractPartitionMap(FSMProxy):
|
||||||
msg = 'The partition {partition} prevents the unmap procedure'.format(partition=partition)
|
msg = 'The partition {partition} prevents the unmap procedure'.format(partition=partition)
|
||||||
raise PartitionError(msg)
|
raise PartitionError(msg)
|
||||||
# Actually unmap the partitions
|
# Actually unmap the partitions
|
||||||
log_check_call(['kpartx', '-d', volume.device_path])
|
log_check_call(['kpartx', '-ds', volume.device_path])
|
||||||
# Call unmap on all partitions
|
# Call unmap on all partitions
|
||||||
for partition in self.partitions:
|
for partition in self.partitions:
|
||||||
partition.unmap()
|
partition.unmap()
|
||||||
|
|
82
bootstrapvz/providers/gce/__init__.py
Normal file
82
bootstrapvz/providers/gce/__init__.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import tasks.apt
|
||||||
|
import tasks.boot
|
||||||
|
import tasks.configuration
|
||||||
|
import tasks.image
|
||||||
|
import tasks.host
|
||||||
|
import tasks.packages
|
||||||
|
from bootstrapvz.common.tasks import volume
|
||||||
|
from bootstrapvz.common.tasks import loopback
|
||||||
|
from bootstrapvz.common.tasks import partitioning
|
||||||
|
from bootstrapvz.common.tasks import filesystem
|
||||||
|
from bootstrapvz.common.tasks import security
|
||||||
|
from bootstrapvz.common.tasks import network
|
||||||
|
from bootstrapvz.common.tasks import initd
|
||||||
|
from bootstrapvz.common.tasks import workspace
|
||||||
|
|
||||||
|
|
||||||
|
def initialize():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def validate_manifest(data, validator, error):
|
||||||
|
import os.path
|
||||||
|
schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.json'))
|
||||||
|
validator(data, schema_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_tasks(tasklist, manifest):
|
||||||
|
import bootstrapvz.common.task_groups
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.base_set)
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.volume_set)
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.mounting_set)
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.get_apt_set(manifest))
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.locale_set)
|
||||||
|
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.bootloader_set.get(manifest.system['bootloader']))
|
||||||
|
|
||||||
|
if manifest.volume['partitions']['type'] != 'none':
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.partitioning_set)
|
||||||
|
|
||||||
|
tasklist.update([bootstrapvz.plugins.cloud_init.tasks.AddBackports,
|
||||||
|
loopback.Create,
|
||||||
|
tasks.apt.SetPackageRepositories,
|
||||||
|
tasks.apt.ImportGoogleKey,
|
||||||
|
tasks.packages.DefaultPackages,
|
||||||
|
tasks.packages.GooglePackages,
|
||||||
|
tasks.packages.InstallGSUtil,
|
||||||
|
|
||||||
|
tasks.configuration.GatherReleaseInformation,
|
||||||
|
|
||||||
|
security.EnableShadowConfig,
|
||||||
|
network.RemoveDNSInfo,
|
||||||
|
network.RemoveHostname,
|
||||||
|
network.ConfigureNetworkIF,
|
||||||
|
tasks.host.DisableIPv6,
|
||||||
|
tasks.host.SetHostname,
|
||||||
|
tasks.boot.ConfigureGrub,
|
||||||
|
initd.AddSSHKeyGeneration,
|
||||||
|
initd.InstallInitScripts,
|
||||||
|
tasks.apt.CleanGoogleRepositoriesAndKeys,
|
||||||
|
|
||||||
|
loopback.MoveImage,
|
||||||
|
tasks.image.CreateTarball,
|
||||||
|
])
|
||||||
|
|
||||||
|
if 'gcs_destination' in manifest.image:
|
||||||
|
tasklist.add(tasks.image.UploadImage)
|
||||||
|
if 'gce_project' in manifest.image:
|
||||||
|
tasklist.add(tasks.image.RegisterImage)
|
||||||
|
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.get_fs_specific_set(manifest.volume['partitions']))
|
||||||
|
|
||||||
|
if 'boot' in manifest.volume['partitions']:
|
||||||
|
tasklist.update(bootstrapvz.common.task_groups.boot_partition_set)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_rollback_tasks(tasklist, manifest, counter_task):
|
||||||
|
counter_task(loopback.Create, volume.Delete)
|
||||||
|
counter_task(filesystem.CreateMountDir, filesystem.DeleteMountDir)
|
||||||
|
counter_task(partitioning.MapPartitions, partitioning.UnmapPartitions)
|
||||||
|
counter_task(filesystem.MountRoot, filesystem.UnmountRoot)
|
||||||
|
counter_task(volume.Attach, volume.Detach)
|
||||||
|
counter_task(workspace.CreateWorkspace, workspace.DeleteWorkspace)
|
43
bootstrapvz/providers/gce/manifest-schema.json
Normal file
43
bootstrapvz/providers/gce/manifest-schema.json
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"title": "GCE manifest",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"image": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"gcs_destination": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"gce_project": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bootloader": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["grub", "extlinux"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"volume": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"partitions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "enum": ["msdos"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["partitions"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
0
bootstrapvz/providers/gce/tasks/__init__.py
Normal file
0
bootstrapvz/providers/gce/tasks/__init__.py
Normal file
56
bootstrapvz/providers/gce/tasks/apt.py
Normal file
56
bootstrapvz/providers/gce/tasks/apt.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tasks import apt
|
||||||
|
from bootstrapvz.common.tools import log_check_call
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class SetPackageRepositories(Task):
|
||||||
|
description = 'Adding apt sources'
|
||||||
|
phase = phases.preparation
|
||||||
|
successors = [apt.AddManifestSources]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
sections = 'main'
|
||||||
|
if 'sections' in info.manifest.system:
|
||||||
|
sections = ' '.join(info.manifest.system['sections'])
|
||||||
|
info.source_lists.add('main', 'deb http://http.debian.net/debian {system.release} ' + sections)
|
||||||
|
info.source_lists.add('main', 'deb-src http://http.debian.net/debian {system.release} ' + sections)
|
||||||
|
info.source_lists.add('backports', 'deb http://http.debian.net/debian {system.release}-backports ' + sections)
|
||||||
|
info.source_lists.add('backports', 'deb-src http://http.debian.net/debian {system.release}-backports ' + sections)
|
||||||
|
info.source_lists.add('goog', 'deb http://goog-repo.appspot.com/debian pigeon main')
|
||||||
|
|
||||||
|
|
||||||
|
class ImportGoogleKey(Task):
|
||||||
|
description = 'Adding Google key'
|
||||||
|
phase = phases.package_installation
|
||||||
|
predecessors = [apt.InstallTrustedKeys]
|
||||||
|
successors = [apt.WriteSources]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
key_file = os.path.join(info.root, 'google.gpg.key')
|
||||||
|
log_check_call(['wget', 'https://goog-repo.appspot.com/debian/key/public.gpg.key', '-O', key_file])
|
||||||
|
log_check_call(['chroot', info.root, 'apt-key', 'add', 'google.gpg.key'])
|
||||||
|
os.remove(key_file)
|
||||||
|
|
||||||
|
|
||||||
|
class CleanGoogleRepositoriesAndKeys(Task):
|
||||||
|
description = 'Removing Google key and apt source files'
|
||||||
|
phase = phases.system_cleaning
|
||||||
|
successors = [apt.AptClean]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
keys = log_check_call(['chroot', info.root, 'apt-key',
|
||||||
|
'adv', '--with-colons', '--list-keys'])
|
||||||
|
# protect against first lines with debug information,
|
||||||
|
# not apt-key output
|
||||||
|
key_id = [key.split(':')[4] for key in keys
|
||||||
|
if len(key.split(':')) == 13 and
|
||||||
|
key.split(':')[9].find('@google.com') > 0]
|
||||||
|
log_check_call(['chroot', info.root, 'apt-key', 'del', key_id[0]])
|
||||||
|
apt_file = os.path.join(info.root, 'etc/apt/sources.list.d/goog.list')
|
||||||
|
os.remove(apt_file)
|
||||||
|
log_check_call(['chroot', info.root, 'apt-get', 'update'])
|
17
bootstrapvz/providers/gce/tasks/boot.py
Normal file
17
bootstrapvz/providers/gce/tasks/boot.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tasks import boot
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigureGrub(Task):
|
||||||
|
description = 'Change grub configuration to allow for ttyS0 output'
|
||||||
|
phase = phases.system_modification
|
||||||
|
successors = [boot.InstallGrub]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
from bootstrapvz.common.tools import sed_i
|
||||||
|
grub_config = os.path.join(info.root, 'etc/default/grub')
|
||||||
|
sed_i(grub_config, r'^(GRUB_CMDLINE_LINUX*=".*)"\s*$', r'\1console=ttyS0,38400n8"')
|
||||||
|
sed_i(grub_config, r'^.*(GRUB_TIMEOUT=).*$', r'GRUB_TIMEOUT=0')
|
17
bootstrapvz/providers/gce/tasks/configuration.py
Normal file
17
bootstrapvz/providers/gce/tasks/configuration.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tools import log_check_call
|
||||||
|
|
||||||
|
|
||||||
|
class GatherReleaseInformation(Task):
|
||||||
|
description = 'Gathering release information about created image'
|
||||||
|
phase = phases.system_modification
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
lsb_distribution = log_check_call(['chroot', info.root, 'lsb_release', '-i', '-s'])
|
||||||
|
lsb_description = log_check_call(['chroot', info.root, 'lsb_release', '-d', '-s'])
|
||||||
|
lsb_release = log_check_call(['chroot', info.root, 'lsb_release', '-r', '-s'])
|
||||||
|
info._gce['lsb_distribution'] = lsb_distribution[0]
|
||||||
|
info._gce['lsb_description'] = lsb_description[0]
|
||||||
|
info._gce['lsb_release'] = lsb_release[0]
|
28
bootstrapvz/providers/gce/tasks/host.py
Normal file
28
bootstrapvz/providers/gce/tasks/host.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tasks import network
|
||||||
|
from bootstrapvz.common.tools import log_check_call
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
class DisableIPv6(Task):
|
||||||
|
description = "Disabling IPv6 support"
|
||||||
|
phase = phases.system_modification
|
||||||
|
predecessors = [network.ConfigureNetworkIF]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
network_configuration_path = os.path.join(info.root, 'etc/sysctl.d/70-disable-ipv6.conf')
|
||||||
|
with open(network_configuration_path, 'w') as config_file:
|
||||||
|
print >>config_file, "net.ipv6.conf.all.disable_ipv6 = 1"
|
||||||
|
|
||||||
|
|
||||||
|
class SetHostname(Task):
|
||||||
|
description = "Setting hostname"
|
||||||
|
phase = phases.system_modification
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
log_check_call(['chroot', info.root, 'ln', '-s',
|
||||||
|
'/usr/share/google/set-hostname',
|
||||||
|
'/etc/dhcp/dhclient-exit-hooks.d/set-hostname'])
|
61
bootstrapvz/providers/gce/tasks/image.py
Normal file
61
bootstrapvz/providers/gce/tasks/image.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tasks import loopback
|
||||||
|
from bootstrapvz.common.tools import log_check_call
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
class CreateTarball(Task):
|
||||||
|
description = 'Creating tarball with image'
|
||||||
|
phase = phases.image_registration
|
||||||
|
predecessors = [loopback.MoveImage]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
import datetime
|
||||||
|
image_name = info.manifest.image['name'].format(**info.manifest_vars)
|
||||||
|
filename = '{image_name}.{ext}'.format(image_name=image_name, ext=info.volume.extension)
|
||||||
|
today = datetime.datetime.today()
|
||||||
|
name_suffix = today.strftime('%Y%m%d')
|
||||||
|
image_name_format = '{lsb_distribution}-{lsb_release}-{release}-v{name_suffix}'
|
||||||
|
image_name = image_name_format.format(lsb_distribution=info._gce['lsb_distribution'],
|
||||||
|
lsb_release=info._gce['lsb_release'],
|
||||||
|
release=info.manifest.system['release'],
|
||||||
|
name_suffix=name_suffix)
|
||||||
|
# ensure that we do not use disallowed characters in image name
|
||||||
|
image_name = image_name.lower()
|
||||||
|
image_name = image_name.replace(".", "-")
|
||||||
|
info._gce['image_name'] = image_name
|
||||||
|
tarball_name = '{image_name}.tar.gz'.format(image_name=image_name)
|
||||||
|
tarball_path = os.path.join(info.manifest.bootstrapper['workspace'], tarball_name)
|
||||||
|
info._gce['tarball_name'] = tarball_name
|
||||||
|
info._gce['tarball_path'] = tarball_path
|
||||||
|
log_check_call(['tar', '--sparse', '-C', info.manifest.bootstrapper['workspace'],
|
||||||
|
'-caf', tarball_path, filename])
|
||||||
|
|
||||||
|
|
||||||
|
class UploadImage(Task):
|
||||||
|
description = 'Uploading image to GSE'
|
||||||
|
phase = phases.image_registration
|
||||||
|
predecessors = [CreateTarball]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
log_check_call(['gsutil', 'cp', info._gce['tarball_path'],
|
||||||
|
info.manifest.image['gcs_destination'] + info._gce['tarball_name']])
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterImage(Task):
|
||||||
|
description = 'Registering image with GCE'
|
||||||
|
phase = phases.image_registration
|
||||||
|
predecessors = [UploadImage]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
image_description = info._gce['lsb_description']
|
||||||
|
if 'description' in info.manifest.image:
|
||||||
|
image_description = info.manifest.image['description']
|
||||||
|
log_check_call(['gcutil', '--project={}'.format(info.manifest.image['gce_project']),
|
||||||
|
'addimage', info._gce['image_name'],
|
||||||
|
info.manifest.image['gcs_destination'] + info._gce['tarball_name'],
|
||||||
|
'--description={}'.format(image_description)])
|
56
bootstrapvz/providers/gce/tasks/packages.py
Normal file
56
bootstrapvz/providers/gce/tasks/packages.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from bootstrapvz.base import Task
|
||||||
|
from bootstrapvz.common import phases
|
||||||
|
from bootstrapvz.common.tasks import apt
|
||||||
|
from bootstrapvz.common.tools import log_check_call
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultPackages(Task):
|
||||||
|
description = 'Adding image packages required for GCE'
|
||||||
|
phase = phases.preparation
|
||||||
|
predecessors = [apt.AddDefaultSources]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
info.packages.add('python')
|
||||||
|
info.packages.add('sudo')
|
||||||
|
info.packages.add('ntp')
|
||||||
|
info.packages.add('lsb-release')
|
||||||
|
info.packages.add('acpi-support-base')
|
||||||
|
info.packages.add('openssh-client')
|
||||||
|
info.packages.add('openssh-server')
|
||||||
|
info.packages.add('dhcpd')
|
||||||
|
|
||||||
|
kernel_packages_path = os.path.join(os.path.dirname(__file__), '../../ec2/tasks/packages-kernels.json')
|
||||||
|
from bootstrapvz.common.tools import config_get
|
||||||
|
kernel_package = config_get(kernel_packages_path, [info.release_codename,
|
||||||
|
info.manifest.system['architecture']])
|
||||||
|
info.packages.add(kernel_package)
|
||||||
|
|
||||||
|
|
||||||
|
class GooglePackages(Task):
|
||||||
|
description = 'Adding image packages required for GCE from Google repositories'
|
||||||
|
phase = phases.preparation
|
||||||
|
predecessors = [DefaultPackages]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
info.packages.add('google-compute-daemon')
|
||||||
|
info.packages.add('google-startup-scripts')
|
||||||
|
info.packages.add('python-gcimagebundle')
|
||||||
|
info.packages.add('gcutil')
|
||||||
|
|
||||||
|
|
||||||
|
class InstallGSUtil(Task):
|
||||||
|
description = 'Install gsutil, not yet packaged'
|
||||||
|
phase = phases.package_installation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, info):
|
||||||
|
log_check_call(['wget', 'http://storage.googleapis.com/pub/gsutil.tar.gz'])
|
||||||
|
gsutil_directory = os.path.join(info.root, 'usr/local/share/google')
|
||||||
|
gsutil_binary = os.path.join(os.path.join(info.root, 'usr/local/bin'), 'gsutil')
|
||||||
|
os.makedirs(gsutil_directory)
|
||||||
|
log_check_call(['tar', 'xaf', 'gsutil.tar.gz', '-C', gsutil_directory])
|
||||||
|
log_check_call(['ln', '-s', '../share/google/gsutil/gsutil', gsutil_binary])
|
46
manifests/gce.manifest.json
Normal file
46
manifests/gce.manifest.json
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"provider": "gce",
|
||||||
|
"bootstrapper": {
|
||||||
|
"workspace": "/target"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"name": "disk",
|
||||||
|
"description": "Debian {system.release} {system.architecture}"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"release": "wheezy",
|
||||||
|
"sections": ["main", "contrib", "non-free"],
|
||||||
|
"architecture": "amd64",
|
||||||
|
"bootloader": "grub",
|
||||||
|
"timezone": "UTC",
|
||||||
|
"locale": "en_US",
|
||||||
|
"charmap": "UTF-8"
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"mirror": "http://gce_debian_mirror.storage.googleapis.com/",
|
||||||
|
"preferences": {
|
||||||
|
"backport-kernel": [
|
||||||
|
{
|
||||||
|
"package": "linux-image-* initramfs-tools",
|
||||||
|
"pin": "release n=wheezy-backports",
|
||||||
|
"pin-priority": 500
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"ntp": {
|
||||||
|
"servers": ["metadata.google.internal"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"volume": {
|
||||||
|
"backing": "raw",
|
||||||
|
"partitions": {
|
||||||
|
"type": "msdos",
|
||||||
|
"root": {
|
||||||
|
"size": "10GiB",
|
||||||
|
"filesystem": "ext4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue