PackerFiles 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/PackerFiles.gemspec +36 -0
- data/README.md +32 -0
- data/Rakefile +21 -0
- data/bin/pkf +5 -0
- data/lib/PackerFiles/CLI/OS.rb +44 -0
- data/lib/PackerFiles/CLI/Root.rb +41 -0
- data/lib/PackerFiles/Core/Base.rb +23 -0
- data/lib/PackerFiles/Core/CDImage.rb +185 -0
- data/lib/PackerFiles/Core/Disk.rb +135 -0
- data/lib/PackerFiles/Core/Exceptions.rb +65 -0
- data/lib/PackerFiles/Core/Hypervisor.rb +96 -0
- data/lib/PackerFiles/Core/Keyboard.rb +38 -0
- data/lib/PackerFiles/Core/Locale.rb +62 -0
- data/lib/PackerFiles/Core/LogicalVolume.rb +41 -0
- data/lib/PackerFiles/Core/Network.rb +67 -0
- data/lib/PackerFiles/Core/Packages.rb +30 -0
- data/lib/PackerFiles/Core/Partition.rb +41 -0
- data/lib/PackerFiles/Core/TimeZone.rb +45 -0
- data/lib/PackerFiles/Core/User.rb +46 -0
- data/lib/PackerFiles/Core/VolumeGroup.rb +50 -0
- data/lib/PackerFiles/Core/example/CDImage.txt +75 -0
- data/lib/PackerFiles/Core/example/Disk.txt +29 -0
- data/lib/PackerFiles/Core/example/Hypervisor.txt +111 -0
- data/lib/PackerFiles/Core/example/Keyboard.txt +28 -0
- data/lib/PackerFiles/Core/example/Locale.txt +31 -0
- data/lib/PackerFiles/Core/example/Network.txt +68 -0
- data/lib/PackerFiles/Core/example/Packages.txt +22 -0
- data/lib/PackerFiles/Core/example/TimeZone.txt +36 -0
- data/lib/PackerFiles/Core/example/User.txt +48 -0
- data/lib/PackerFiles/OS/Builder.rb +116 -0
- data/lib/PackerFiles/OS/CentOS/CD.rb +81 -0
- data/lib/PackerFiles/OS/CentOS/Kickstart.rb +64 -0
- data/lib/PackerFiles/OS/CentOS/Server.rb +170 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Base_erb.rb +4 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Disk_erb.rb +35 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Keyboard_erb.rb +4 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Locale_erb.rb +4 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Network_erb.rb +17 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Packages_erb.rb +34 -0
- data/lib/PackerFiles/OS/CentOS/Templates/Tail_erb.rb +15 -0
- data/lib/PackerFiles/OS/CentOS/Templates/TimeZone_erb.rb +10 -0
- data/lib/PackerFiles/OS/CentOS/Templates/User_erb.rb +20 -0
- data/lib/PackerFiles/OS/CentOS/Templates/boot_command_erb.rb +16 -0
- data/lib/PackerFiles/OS/CentOS/Templates/command_as_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/CentOS/Templates/disable_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/CentOS/Templates/enable_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/CentOS/Templates/shutdown_command_erb.rb +1 -0
- data/lib/PackerFiles/OS/Debian/Apt.rb +42 -0
- data/lib/PackerFiles/OS/Debian/CD.rb +65 -0
- data/lib/PackerFiles/OS/Debian/Mirrors.rb +73 -0
- data/lib/PackerFiles/OS/Debian/Preseed.rb +64 -0
- data/lib/PackerFiles/OS/Debian/Server.rb +169 -0
- data/lib/PackerFiles/OS/Debian/Templates/Apt_erb.rb +18 -0
- data/lib/PackerFiles/OS/Debian/Templates/Disk_erb.rb +69 -0
- data/lib/PackerFiles/OS/Debian/Templates/Keyboard_erb.rb +5 -0
- data/lib/PackerFiles/OS/Debian/Templates/Locale_erb.rb +7 -0
- data/lib/PackerFiles/OS/Debian/Templates/Mirrors_erb.rb +9 -0
- data/lib/PackerFiles/OS/Debian/Templates/Network_erb.rb +19 -0
- data/lib/PackerFiles/OS/Debian/Templates/Packages_erb.rb +6 -0
- data/lib/PackerFiles/OS/Debian/Templates/TimeZone_erb.rb +6 -0
- data/lib/PackerFiles/OS/Debian/Templates/User_erb.rb +14 -0
- data/lib/PackerFiles/OS/Debian/Templates/boot_command_erb.rb +24 -0
- data/lib/PackerFiles/OS/Debian/Templates/command_as_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/Debian/Templates/disable_root_erb.rb +5 -0
- data/lib/PackerFiles/OS/Debian/Templates/enable_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/Debian/Templates/shutdown_command_erb.rb +1 -0
- data/lib/PackerFiles/OS/Debian/example/Apt.txt +55 -0
- data/lib/PackerFiles/OS/DocGenerator.rb +51 -0
- data/lib/PackerFiles/OS/Finder.rb +96 -0
- data/lib/PackerFiles/OS/RHEL/CD.rb +32 -0
- data/lib/PackerFiles/OS/RHEL/Fedora.rb +59 -0
- data/lib/PackerFiles/OS/RHEL/FedoraCD.rb +68 -0
- data/lib/PackerFiles/OS/RHEL/Server.rb +32 -0
- data/lib/PackerFiles/OS/RHEL/Templates/Fedora_Tail_erb.rb +14 -0
- data/lib/PackerFiles/OS/RHEL/Templates/boot_command_fedora_erb.rb +16 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Base_erb.xml +32 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Disk_erb.xml +110 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Keyboard_erb.xml +7 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Locale_erb.xml +18 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Network_erb.xml +143 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Packages_erb.xml +29 -0
- data/lib/PackerFiles/OS/SUSE/Templates/Tail_erb.xml +4 -0
- data/lib/PackerFiles/OS/SUSE/Templates/TimeZone_erb.xml +15 -0
- data/lib/PackerFiles/OS/SUSE/Templates/User_erb.xml +57 -0
- data/lib/PackerFiles/OS/SUSE/Templates/boot_command_erb.rb +23 -0
- data/lib/PackerFiles/OS/SUSE/Templates/command_as_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/SUSE/Templates/disable_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/SUSE/Templates/enable_root_erb.rb +1 -0
- data/lib/PackerFiles/OS/SUSE/Templates/shutdown_command_erb.rb +1 -0
- data/lib/PackerFiles/OS/Ubuntu/CD.rb +81 -0
- data/lib/PackerFiles/OS/Ubuntu/Desktop.rb +50 -0
- data/lib/PackerFiles/OS/Ubuntu/DesktopCD.rb +16 -0
- data/lib/PackerFiles/OS/Ubuntu/Mirrors.rb +103 -0
- data/lib/PackerFiles/OS/Ubuntu/Server.rb +78 -0
- data/lib/PackerFiles/OS/Ubuntu/Templates/Apt_erb.rb +18 -0
- data/lib/PackerFiles/OS/Ubuntu/Templates/boot_command_desktop_erb.rb +36 -0
- data/lib/PackerFiles/OS/Ubuntu/Templates/boot_command_erb.rb +30 -0
- data/lib/PackerFiles/OS/Ubuntu/Templates/ubiquity_erb.rb +8 -0
- data/lib/PackerFiles/OS/example/Doc.txt +8 -0
- data/lib/PackerFiles/Provision/Base.rb +20 -0
- data/lib/PackerFiles/Provision/ChefSolo.rb +90 -0
- data/lib/PackerFiles/Provision/ChefSoloHelper.rb +72 -0
- data/lib/PackerFiles/Provision/Copy.rb +22 -0
- data/lib/PackerFiles/Provision/ExternalScript.rb +43 -0
- data/lib/PackerFiles/Provision/InlineScript.rb +37 -0
- data/lib/PackerFiles/Provision/Provisioners.rb +87 -0
- data/lib/PackerFiles/Provision/example/Provisioners.txt +21 -0
- data/lib/PackerFiles/Utils/AutoZone.rb +96 -0
- data/lib/PackerFiles/Utils/FastestURL.rb +73 -0
- data/lib/PackerFiles/Utils/Generator.rb +34 -0
- data/lib/PackerFiles/Utils/HashSerializer.rb +110 -0
- data/lib/PackerFiles/Utils/Size.rb +44 -0
- data/lib/PackerFiles/Utils/TypeAccessor.rb +71 -0
- data/lib/PackerFiles/Virtual/Hypervisors.rb +72 -0
- data/lib/PackerFiles/Virtual/KVM.rb +119 -0
- data/lib/PackerFiles/Virtual/KVMConverter.rb +79 -0
- data/lib/PackerFiles/Virtual/VMWare.rb +108 -0
- data/lib/PackerFiles/Virtual/VMWareConverter.rb +78 -0
- data/lib/PackerFiles/Virtual/VirtualBox.rb +98 -0
- data/lib/PackerFiles/Virtual/VirtualBoxConverter.rb +75 -0
- data/lib/PackerFiles/Virtual/example/Hypervisors.txt +23 -0
- data/lib/PackerFiles/version.rb +3 -0
- data/lib/PackerFiles.rb +44 -0
- data/spec/core/builder_spec.rb +84 -0
- data/spec/core/cdimage_spec.rb +159 -0
- data/spec/core/disk_spec.rb +191 -0
- data/spec/core/keyboard_spec.rb +36 -0
- data/spec/core/locale_spec.rb +73 -0
- data/spec/core/network_spec.rb +96 -0
- data/spec/core/packages_spec.rb +18 -0
- data/spec/core/timezone_spec.rb +43 -0
- data/spec/core/user_spec.rb +44 -0
- data/spec/core/volume_group_spec.rb +53 -0
- data/spec/os/centos/cd_spec.rb +12 -0
- data/spec/os/centos/data/Server-Script-Provisioner.rb +57 -0
- data/spec/os/centos/data/Server.rb +55 -0
- data/spec/os/centos/data/chef-solo-provisioner.rb +58 -0
- data/spec/os/centos/finder_spec.rb +22 -0
- data/spec/os/centos/templates/boot_command_spec.rb +57 -0
- data/spec/os/centos/templates/disk_spec.rb +79 -0
- data/spec/os/centos/templates/keyboard_spec.rb +18 -0
- data/spec/os/centos/templates/locale_spec.rb +26 -0
- data/spec/os/centos/templates/network_spec.rb +38 -0
- data/spec/os/centos/templates/packages_spec.rb +18 -0
- data/spec/os/centos/templates/timezone_spec.rb +37 -0
- data/spec/os/centos/templates/user_spec.rb +24 -0
- data/spec/os/debian/apt_spec.rb +50 -0
- data/spec/os/debian/cd_spec.rb +30 -0
- data/spec/os/debian/data/7zip.tar.gz +0 -0
- data/spec/os/debian/data/All-sections.rb +60 -0
- data/spec/os/debian/data/Chef-Solo-Provisioner.rb +60 -0
- data/spec/os/debian/data/Copy-Provisioner.rb +61 -0
- data/spec/os/debian/data/External-Script-Provisioner.rb +69 -0
- data/spec/os/debian/data/Missing-CDImage.rb +53 -0
- data/spec/os/debian/data/Missing-VMWare-KVM-sections.rb +51 -0
- data/spec/os/debian/data/Missing-sections.rb +53 -0
- data/spec/os/debian/data/Script-Provisioner.rb +62 -0
- data/spec/os/debian/data/chef-solo-cookbooks/git_setup/Berksfile.lock +32 -0
- data/spec/os/debian/data/chef-solo-cookbooks/git_setup/README.md +81 -0
- data/spec/os/debian/data/chef-solo-cookbooks/git_setup/attributes/default.rb +3 -0
- data/spec/os/debian/data/chef-solo-cookbooks/git_setup/metadata.rb +11 -0
- data/spec/os/debian/data/chef-solo-cookbooks/git_setup/recipes/default.rb +30 -0
- data/spec/os/debian/data/chef-solo.json +5 -0
- data/spec/os/debian/data/test-script.sh +4 -0
- data/spec/os/debian/finder_spec.rb +59 -0
- data/spec/os/debian/mirrors_spec.rb +18 -0
- data/spec/os/debian/preseed_spec.rb +56 -0
- data/spec/os/debian/server_spec.rb +59 -0
- data/spec/os/debian/templates/apt_spec.rb +20 -0
- data/spec/os/debian/templates/boot_command_spec.rb +56 -0
- data/spec/os/debian/templates/disk_spec.rb +81 -0
- data/spec/os/debian/templates/keyboard_spec.rb +18 -0
- data/spec/os/debian/templates/locale_spec.rb +36 -0
- data/spec/os/debian/templates/mirrors_spec.rb +16 -0
- data/spec/os/debian/templates/network_spec.rb +38 -0
- data/spec/os/debian/templates/packages_spec.rb +17 -0
- data/spec/os/debian/templates/timezone_spec.rb +36 -0
- data/spec/os/debian/templates/user_spec.rb +25 -0
- data/spec/os/finder_spec.rb +21 -0
- data/spec/os/rhel/data/Fedora.rb +45 -0
- data/spec/os/rhel/data/Server-Custom.rb +54 -0
- data/spec/os/rhel/data/Server.rb +45 -0
- data/spec/os/rhel/finder_spec.rb +21 -0
- data/spec/os/ubuntu/cd_spec.rb +28 -0
- data/spec/os/ubuntu/data/Desktop.rb +61 -0
- data/spec/os/ubuntu/data/Server.rb +61 -0
- data/spec/os/ubuntu/finder_spec.rb +17 -0
- data/spec/os/ubuntu/mirrors_spec.rb +18 -0
- data/spec/utils/autozone_spec.rb +33 -0
- data/spec/virtual/virtual_box_spec.rb +16 -0
- metadata +405 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Core/Hypervisor'
|
|
3
|
+
require 'Utils/HashSerializer'
|
|
4
|
+
require 'Utils/Size'
|
|
5
|
+
|
|
6
|
+
module PackerFiles
|
|
7
|
+
|
|
8
|
+
module Virtual
|
|
9
|
+
|
|
10
|
+
# Abstraction for KVM(QEMU) builder in Packer. Refer to Packer.io
|
|
11
|
+
# documentation on more details on the various variables for
|
|
12
|
+
# QEMU ISO Builder.
|
|
13
|
+
class KVM < Utils::HashSerializer
|
|
14
|
+
|
|
15
|
+
# Import Size with KVM class as a Mix-in
|
|
16
|
+
include Utils::Size
|
|
17
|
+
|
|
18
|
+
# List of attributes that would be converted into Hash Values. The Hash
|
|
19
|
+
# values will eventually be converted into JSON values.
|
|
20
|
+
hash_variable :iso_checksum
|
|
21
|
+
hash_variable :iso_checksum_type
|
|
22
|
+
hash_variable :iso_url
|
|
23
|
+
hash_variable :ssh_username
|
|
24
|
+
hash_variable :accelerator
|
|
25
|
+
hash_variable :boot_command, Array
|
|
26
|
+
hash_variable :boot_wait
|
|
27
|
+
hash_variable :disk_size, Integer
|
|
28
|
+
hash_variable :disk_interface
|
|
29
|
+
hash_variable :floppy_files, Array
|
|
30
|
+
hash_variable :format
|
|
31
|
+
hash_variable :headless
|
|
32
|
+
hash_variable :http_directory
|
|
33
|
+
hash_variable :http_port_min, Integer
|
|
34
|
+
hash_variable :http_port_max, Integer
|
|
35
|
+
hash_variable :iso_urls, Array
|
|
36
|
+
hash_variable :net_device
|
|
37
|
+
hash_variable :output_directory
|
|
38
|
+
hash_variable :qemuargs, Array
|
|
39
|
+
hash_variable :qemu_binary
|
|
40
|
+
hash_variable :shutdown_command
|
|
41
|
+
hash_variable :shutdown_timeout
|
|
42
|
+
hash_variable :ssh_host_port_min, Integer
|
|
43
|
+
hash_variable :ssh_host_port_max, Integer
|
|
44
|
+
hash_variable :ssh_key_path
|
|
45
|
+
hash_variable :ssh_password
|
|
46
|
+
hash_variable :ssh_port, Integer
|
|
47
|
+
hash_variable :ssh_wait_timeout
|
|
48
|
+
hash_variable :vm_name
|
|
49
|
+
hash_variable :type
|
|
50
|
+
hash_variable :vnc_port_min
|
|
51
|
+
hash_variable :vnc_port_max
|
|
52
|
+
hash_variable :name
|
|
53
|
+
|
|
54
|
+
# Constructor. Supports the standard block semantics via yield
|
|
55
|
+
def initialize(&block)
|
|
56
|
+
self.type = 'qemu'
|
|
57
|
+
yield self if block_given?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Given an Hypervisor object, convert into a list of qemuargs commands
|
|
61
|
+
def Hypervisor(hyper, amd64=true)
|
|
62
|
+
|
|
63
|
+
# Allocate a new array to store values.
|
|
64
|
+
value = Array.new
|
|
65
|
+
|
|
66
|
+
# If CPU hot plug is enabled, then current cpu count < max cpu count
|
|
67
|
+
count = hyper.cpu_count.to_s
|
|
68
|
+
if (hyper.cpu_hot_plug)
|
|
69
|
+
value << ["-smp", "cpus=#{count},maxcpus=255"]
|
|
70
|
+
else
|
|
71
|
+
value << ["-smp", "cpus=#{count},maxcpus=#{count}"]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# NOTE: CPU Execution Cap is not implemented in KVM
|
|
75
|
+
# NOTE: Video RAM Size is not implemented in KVM
|
|
76
|
+
|
|
77
|
+
# RAM Size is always translated into MiB
|
|
78
|
+
value << ["-m", MiB(hyper.ram_size).to_s]
|
|
79
|
+
|
|
80
|
+
# Memory Balloon size is ignored by KVM. It is just enabling or
|
|
81
|
+
# disabling a feature.
|
|
82
|
+
if MiB(hyper.guest_balloon_size) > 0
|
|
83
|
+
value << ["-balloon", "none"]
|
|
84
|
+
else
|
|
85
|
+
value << ["-balloon", "virtio"]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# ACPI is by default enabled on KVM. We just have to handle the
|
|
89
|
+
# disabled case.
|
|
90
|
+
if !hyper.acpi_enabled
|
|
91
|
+
value << ["-no-acpi"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# We have to use EFI firmware for virtual machines, if it has been
|
|
95
|
+
# asked for.
|
|
96
|
+
if hyper.use_efi
|
|
97
|
+
value << ["-bios", "/usr/share/ovmf/OVMF.fd"]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# KVM works very differently w.r.t. CPU features. First a base CPU
|
|
101
|
+
# (64 bit or 32 bit) has to be selected and then more features need
|
|
102
|
+
# to be added.
|
|
103
|
+
cpu_features = (amd64)?'qemu64':'qemu32'
|
|
104
|
+
cpu_features += ',+pae' if hyper.pae_enabled
|
|
105
|
+
cpu_features += ',+vmx' if hyper.hw_virt_enabled
|
|
106
|
+
cpu_features += ',+npt' if hyper.hw_nested_pages
|
|
107
|
+
cpu_features += ',+pse36' if hyper.use_large_pages
|
|
108
|
+
cpu_features += ',+hypervisor' if hyper.unrestricted_guest_mode
|
|
109
|
+
value << ["-cpu", cpu_features]
|
|
110
|
+
|
|
111
|
+
# Set the value for QEMU Args
|
|
112
|
+
self.qemuargs = value
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end # Virtual
|
|
118
|
+
|
|
119
|
+
end # PackerFiles
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Virtual/KVM'
|
|
3
|
+
require 'Utils/Generator'
|
|
4
|
+
|
|
5
|
+
# Converts a given OS into a KVM builder
|
|
6
|
+
module PackerFiles
|
|
7
|
+
module Virtual
|
|
8
|
+
class KVMConverter
|
|
9
|
+
|
|
10
|
+
# Hash Value
|
|
11
|
+
attr_reader :value
|
|
12
|
+
|
|
13
|
+
# Constructor
|
|
14
|
+
def initialize(os, gen)
|
|
15
|
+
@value = convert(os, gen).to_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Convert OS object into a hash
|
|
19
|
+
def convert(os, gen)
|
|
20
|
+
KVM.new do |kvm|
|
|
21
|
+
|
|
22
|
+
# Fill in output directory
|
|
23
|
+
rel = os.CDImage.release
|
|
24
|
+
arch = os.CDImage.arch
|
|
25
|
+
name = os.name.gsub('::', '-')
|
|
26
|
+
kvm.output_directory = "output-#{name}-#{rel}-#{arch}-#{kvm.type}"
|
|
27
|
+
kvm.name = "#{name}-#{rel}-#{arch}-#{kvm.type}"
|
|
28
|
+
|
|
29
|
+
# Fill in image details, Some amount of url handling
|
|
30
|
+
# is really required to handle various corner cases.
|
|
31
|
+
# thus making the code a bit harder to understand
|
|
32
|
+
kvm.iso_checksum_type = os.CDImage.check_sum_type
|
|
33
|
+
kvm.iso_checksum = os.CDImage.check_sum
|
|
34
|
+
|
|
35
|
+
# The code below creates a user variable called mirror
|
|
36
|
+
# whose base value is the URL fragment w/o the file-name.
|
|
37
|
+
url = URI::parse(os.CDImage.iso_url)
|
|
38
|
+
file = File.basename(url.path)
|
|
39
|
+
url.path = File.dirname(url.path)
|
|
40
|
+
gen.variables['mirror'] = url.to_s
|
|
41
|
+
kvm.iso_url = "{{user `mirror`}}/#{file}"
|
|
42
|
+
|
|
43
|
+
# Fill in SSH specifics
|
|
44
|
+
kvm.ssh_username = os.User.login
|
|
45
|
+
kvm.ssh_password = os.User.password
|
|
46
|
+
kvm.ssh_wait_timeout = '10000s'
|
|
47
|
+
|
|
48
|
+
# Fill in other specifics
|
|
49
|
+
kvm.disk_size = os.Disk.size
|
|
50
|
+
kvm.http_directory = File.basename(os.http_dir)
|
|
51
|
+
|
|
52
|
+
# Fill in boot command and boot_wait command
|
|
53
|
+
gen.variables['host'] = ''
|
|
54
|
+
gen.variables['port'] = ''
|
|
55
|
+
user_var_name = name + "-#{kvm.type}-boot-wait"
|
|
56
|
+
gen.variables[user_var_name] = "10s"
|
|
57
|
+
kvm.boot_command = os.boot_command
|
|
58
|
+
kvm.boot_wait = "{{user `#{user_var_name}`}}"
|
|
59
|
+
|
|
60
|
+
# Fill in shutdown command
|
|
61
|
+
kvm.shutdown_command = os.shutdown_command
|
|
62
|
+
|
|
63
|
+
# If the guest OS type contains 64 bit OS, then we need to enable
|
|
64
|
+
# a 64 bit CPU.
|
|
65
|
+
if os.guest_os_type.match("64")
|
|
66
|
+
kvm.Hypervisor(os.Hypervisors.KVM, true)
|
|
67
|
+
else
|
|
68
|
+
kvm.Hypervisor(os.Hypervisors.KVM, false)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Merge with Custom attributes
|
|
72
|
+
kvm.merge_hs(os.Hypervisors.KVM.custom)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Core/Hypervisor'
|
|
3
|
+
require 'Utils/HashSerializer'
|
|
4
|
+
require 'Utils/Size'
|
|
5
|
+
|
|
6
|
+
module PackerFiles
|
|
7
|
+
|
|
8
|
+
module Virtual
|
|
9
|
+
|
|
10
|
+
# Abstraction for VMWare builder in Packer. Refer to Packer.io
|
|
11
|
+
# documentation on more details on the various variables for
|
|
12
|
+
# VMWare ISO Builder.
|
|
13
|
+
class VMWare < Utils::HashSerializer
|
|
14
|
+
|
|
15
|
+
# Import Size with VirtualBox class as a Mix-in
|
|
16
|
+
include Utils::Size
|
|
17
|
+
|
|
18
|
+
# List of attributes that would be converted into Hash Values. The Hash
|
|
19
|
+
# values will eventually be converted into JSON values.
|
|
20
|
+
hash_variable :iso_checksum
|
|
21
|
+
hash_variable :iso_checksum_type
|
|
22
|
+
hash_variable :iso_url
|
|
23
|
+
hash_variable :ssh_username
|
|
24
|
+
hash_variable :boot_command, Array
|
|
25
|
+
hash_variable :boot_wait
|
|
26
|
+
hash_variable :disk_size, Integer
|
|
27
|
+
hash_variable :disk_type_id
|
|
28
|
+
hash_variable :floppy_files, Array
|
|
29
|
+
hash_variable :fusion_app_path
|
|
30
|
+
hash_variable :guest_os_type
|
|
31
|
+
hash_variable :headless
|
|
32
|
+
hash_variable :http_directory
|
|
33
|
+
hash_variable :http_port_min, Integer
|
|
34
|
+
hash_variable :http_port_max, Integer
|
|
35
|
+
hash_variable :iso_urls, Array
|
|
36
|
+
hash_variable :output_directory
|
|
37
|
+
hash_variable :remote_cache_datastore
|
|
38
|
+
hash_variable :remote_cache_directory
|
|
39
|
+
hash_variable :remote_datastore
|
|
40
|
+
hash_variable :remote_host
|
|
41
|
+
hash_variable :remote_password
|
|
42
|
+
hash_variable :remote_type
|
|
43
|
+
hash_variable :remote_username
|
|
44
|
+
hash_variable :shutdown_command
|
|
45
|
+
hash_variable :shutdown_timeout
|
|
46
|
+
hash_variable :ssh_host
|
|
47
|
+
hash_variable :ssh_key_path
|
|
48
|
+
hash_variable :ssh_password
|
|
49
|
+
hash_variable :ssh_port, Integer
|
|
50
|
+
hash_variable :ssh_wait_timeout
|
|
51
|
+
hash_variable :tools_upload_flavor
|
|
52
|
+
hash_variable :tools_upload_path
|
|
53
|
+
hash_variable :vm_name
|
|
54
|
+
hash_variable :vmdk_name
|
|
55
|
+
hash_variable :vmx_data, Hash
|
|
56
|
+
hash_variable :vmx_data_post, Hash
|
|
57
|
+
hash_variable :vmx_template_path
|
|
58
|
+
hash_variable :vnc_port_min, Integer
|
|
59
|
+
hash_variable :vnc_port_max, Integer
|
|
60
|
+
hash_variable :type
|
|
61
|
+
hash_variable :name
|
|
62
|
+
|
|
63
|
+
# Constructor. Supports the standard block semantics via yield
|
|
64
|
+
def initialize(&block)
|
|
65
|
+
self.type = 'vmware-iso'
|
|
66
|
+
yield self if block_given?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Given an Hypervisor object, convert into a list of VMX commands
|
|
70
|
+
# and set vmx_data accordingly
|
|
71
|
+
def Hypervisor(hyper)
|
|
72
|
+
value = Hash.new
|
|
73
|
+
value['numvcpus'] = hyper.cpu_count.to_s
|
|
74
|
+
value['vcpu.hotadd'] = true_false(hyper.cpu_hot_plug)
|
|
75
|
+
value['memsize'] = MiB(hyper.ram_size).to_s
|
|
76
|
+
value['mem.hotadd'] = 'TRUE'
|
|
77
|
+
value['svga.vramsize'] = Bytes(MiB(hyper.video_ram_size)).to_s
|
|
78
|
+
value['acpi.present'] = true_false(hyper.acpi_enabled)
|
|
79
|
+
value['paevm'] = true_false(hyper.pae_enabled)
|
|
80
|
+
value['vhv.enable'] = true_false(hyper.hw_virt_enabled)
|
|
81
|
+
value['monitor_control.disable_mmu_largepages'] = true_false(!hyper.use_large_pages)
|
|
82
|
+
value['mks.enable3d'] = true_false(hyper.video_3d_acceleration)
|
|
83
|
+
value['firmware'] = 'efi' if (hyper.use_efi)
|
|
84
|
+
value['sched.mem.maxmemctl'] = MiB(hyper.guest_balloon_size)
|
|
85
|
+
|
|
86
|
+
# CPU Speed limit is done by converting any value into MHZ
|
|
87
|
+
value['sched.cpu.max'] = MiB(hyper.cpu_speed)
|
|
88
|
+
value['sched.cpu.units'] = 'mhz'
|
|
89
|
+
|
|
90
|
+
#NOTE: CPU Execution Cap is not supported in VMX File settings.
|
|
91
|
+
#NOTE: Extended Page Table [Nested Paging] is enabled because of VHV.
|
|
92
|
+
#NOTE: Unrestricted Guest Mode is enabled because of VHV.
|
|
93
|
+
self.vmx_data = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Convert a boolean to a TRUE/FALSE string
|
|
97
|
+
private
|
|
98
|
+
def true_false(value)
|
|
99
|
+
return "TRUE" if (value)
|
|
100
|
+
return "FALSE" if (!value)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end # Virtual
|
|
107
|
+
|
|
108
|
+
end # PackerFiles
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Virtual/VMWare'
|
|
3
|
+
require 'Utils/Generator'
|
|
4
|
+
|
|
5
|
+
# Converts a given OS into a VMWare builder
|
|
6
|
+
module PackerFiles
|
|
7
|
+
module Virtual
|
|
8
|
+
class VMWareConverter
|
|
9
|
+
|
|
10
|
+
# Hash Value
|
|
11
|
+
attr_reader :value
|
|
12
|
+
|
|
13
|
+
# Constructor
|
|
14
|
+
def initialize(os, gen)
|
|
15
|
+
@value = convert(os, gen).to_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Convert OS object into a hash
|
|
19
|
+
def convert(os, gen)
|
|
20
|
+
VMWare.new do |vmware|
|
|
21
|
+
|
|
22
|
+
# Fill in output directory and builder name
|
|
23
|
+
rel = os.CDImage.release
|
|
24
|
+
arch = os.CDImage.arch
|
|
25
|
+
name = os.name.gsub('::', '-')
|
|
26
|
+
vmware.output_directory = "output-#{name}-#{rel}-#{arch}-#{vmware.type}"
|
|
27
|
+
vmware.name = "#{name}-#{rel}-#{arch}-#{vmware.type}"
|
|
28
|
+
|
|
29
|
+
# Fill in image details, Some amount of url handling
|
|
30
|
+
# is really required to handle various corner cases.
|
|
31
|
+
# thus making the code a bit harder to understand
|
|
32
|
+
vmware.iso_checksum_type = os.CDImage.check_sum_type
|
|
33
|
+
vmware.iso_checksum = os.CDImage.check_sum
|
|
34
|
+
|
|
35
|
+
# The code below creates a user variable called mirror
|
|
36
|
+
# whose base value is the URL fragment w/o the file-name.
|
|
37
|
+
url = URI::parse(os.CDImage.iso_url)
|
|
38
|
+
file = File.basename(url.path)
|
|
39
|
+
url.path = File.dirname(url.path)
|
|
40
|
+
gen.variables['mirror'] = url.to_s
|
|
41
|
+
vmware.iso_url = "{{user `mirror`}}/#{file}"
|
|
42
|
+
|
|
43
|
+
# Fill in SSH specifics
|
|
44
|
+
vmware.ssh_username = os.User.login
|
|
45
|
+
vmware.ssh_password = os.User.password
|
|
46
|
+
vmware.ssh_wait_timeout = '10000s'
|
|
47
|
+
|
|
48
|
+
# Fill in other specifics
|
|
49
|
+
vmware.disk_size = os.Disk.size
|
|
50
|
+
vmware.http_directory = File.basename(os.http_dir)
|
|
51
|
+
vmware.guest_os_type = os.vmware_guest_os_type
|
|
52
|
+
|
|
53
|
+
# Fill in boot command and boot_wait timeout
|
|
54
|
+
gen.variables['host'] = ''
|
|
55
|
+
gen.variables['port'] = ''
|
|
56
|
+
user_var_name = name + "-#{vmware.type}-boot-wait"
|
|
57
|
+
gen.variables[user_var_name] = "10s"
|
|
58
|
+
vmware.boot_command = os.boot_command
|
|
59
|
+
vmware.boot_wait = "{{user `#{user_var_name}`}}"
|
|
60
|
+
|
|
61
|
+
# Fill in shutdown command
|
|
62
|
+
vmware.shutdown_command = os.shutdown_command
|
|
63
|
+
|
|
64
|
+
# Generate vmx commands
|
|
65
|
+
vmware.Hypervisor(os.Hypervisors.VMWare)
|
|
66
|
+
|
|
67
|
+
# Tools flavor
|
|
68
|
+
vmware.tools_upload_flavor = os.vmware_guest_os_flavor
|
|
69
|
+
|
|
70
|
+
# Merge with Custom values
|
|
71
|
+
vmware.merge_hs(os.Hypervisors.VMWare.custom)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Core/Hypervisor'
|
|
3
|
+
require 'Utils/HashSerializer'
|
|
4
|
+
require 'Utils/Size'
|
|
5
|
+
|
|
6
|
+
module PackerFiles
|
|
7
|
+
|
|
8
|
+
module Virtual
|
|
9
|
+
|
|
10
|
+
# Abstraction for VirtualBox builder in Packer. Refer to Packer.io
|
|
11
|
+
# documentation on more details on the various variables for
|
|
12
|
+
# VirtualBox ISO Builder.
|
|
13
|
+
class VirtualBox < Utils::HashSerializer
|
|
14
|
+
|
|
15
|
+
# Import Size with VirtualBox class as a Mix-in
|
|
16
|
+
include Utils::Size
|
|
17
|
+
|
|
18
|
+
# List of attributes that would be converted into Hash Values. The Hash
|
|
19
|
+
# values will eventually be converted into JSON values.
|
|
20
|
+
hash_variable :iso_checksum
|
|
21
|
+
hash_variable :iso_checksum_type
|
|
22
|
+
hash_variable :iso_url
|
|
23
|
+
hash_variable :ssh_username
|
|
24
|
+
hash_variable :boot_command, Array
|
|
25
|
+
hash_variable :boot_wait
|
|
26
|
+
hash_variable :disk_size, Integer
|
|
27
|
+
hash_variable :export_opts, Array
|
|
28
|
+
hash_variable :floppy_files, Array
|
|
29
|
+
hash_variable :format
|
|
30
|
+
hash_variable :guest_additions_mode
|
|
31
|
+
hash_variable :guest_additions_path
|
|
32
|
+
hash_variable :guest_additions_sha256
|
|
33
|
+
hash_variable :guest_additions_url
|
|
34
|
+
hash_variable :guest_os_type
|
|
35
|
+
hash_variable :hard_drive_interface
|
|
36
|
+
hash_variable :headless
|
|
37
|
+
hash_variable :http_directory
|
|
38
|
+
hash_variable :http_port_min, Integer
|
|
39
|
+
hash_variable :http_port_max, Integer
|
|
40
|
+
hash_variable :iso_interface
|
|
41
|
+
hash_variable :iso_urls, Array
|
|
42
|
+
hash_variable :output_directory
|
|
43
|
+
hash_variable :shutdown_command
|
|
44
|
+
hash_variable :shutdown_timeout
|
|
45
|
+
hash_variable :ssh_host_port_min, Integer
|
|
46
|
+
hash_variable :ssh_host_port_max, Integer
|
|
47
|
+
hash_variable :ssh_key_path
|
|
48
|
+
hash_variable :ssh_password
|
|
49
|
+
hash_variable :ssh_port, Integer
|
|
50
|
+
hash_variable :ssh_wait_timeout
|
|
51
|
+
hash_variable :vboxmanage, Array
|
|
52
|
+
hash_variable :vboxmanage_post, Array
|
|
53
|
+
hash_variable :virtualbox_version_file
|
|
54
|
+
hash_variable :vm_name
|
|
55
|
+
hash_variable :type
|
|
56
|
+
hash_variable :name
|
|
57
|
+
|
|
58
|
+
# Constructor. Supports the standard block semantics via yield
|
|
59
|
+
def initialize(&block)
|
|
60
|
+
self.type = 'virtualbox-iso'
|
|
61
|
+
yield self if block_given?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Given an Hypervisor object, convert into a list of VBoxmanage commands
|
|
65
|
+
# and set vboxmanage attribute accordingly.
|
|
66
|
+
def Hypervisor(hyper)
|
|
67
|
+
value = Array.new
|
|
68
|
+
std = ["modifyvm", "{{.Name}}"]
|
|
69
|
+
value << std + ["--cpus", hyper.cpu_count.to_s]
|
|
70
|
+
value << std + ["--cpuhotplug", on_off(hyper.cpu_hot_plug)]
|
|
71
|
+
value << std + ["--cpuexecutioncap", hyper.cpu_execution_cap.to_s]
|
|
72
|
+
value << std + ["--memory", MiB(hyper.ram_size).to_s]
|
|
73
|
+
value << std + ["--vram", MiB(hyper.video_ram_size).to_s]
|
|
74
|
+
value << std + ["--acpi", on_off(hyper.acpi_enabled)]
|
|
75
|
+
value << std + ["--pae", on_off(hyper.pae_enabled)]
|
|
76
|
+
value << std + ["--hwvirtex", on_off(hyper.hw_virt_enabled)]
|
|
77
|
+
value << std + ["--nestedpaging", on_off(hyper.hw_virt_enabled)]
|
|
78
|
+
value << std + ["--vtxvpid", on_off(hyper.hw_virt_enabled)]
|
|
79
|
+
value << std + ["--largepages", on_off(hyper.use_large_pages)]
|
|
80
|
+
value << std + ["--vtxux", on_off(hyper.unrestricted_guest_mode)]
|
|
81
|
+
value << std + ["--accelerate3d", on_off(hyper.video_3d_acceleration)]
|
|
82
|
+
value << std + ["--firmware","efi"] if (hyper.use_efi)
|
|
83
|
+
value << std + ["--guestmemoryballoon", MiB(hyper.guest_balloon_size).to_s]
|
|
84
|
+
self.vboxmanage = value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Convert a boolean to a on/off string
|
|
88
|
+
private
|
|
89
|
+
def on_off(value)
|
|
90
|
+
return "on" if (value)
|
|
91
|
+
return "off" if (!value)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end # VirtualBox
|
|
95
|
+
|
|
96
|
+
end # Virtual
|
|
97
|
+
|
|
98
|
+
end # PackerFiles
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'Virtual/VirtualBox'
|
|
3
|
+
require 'Utils/Generator'
|
|
4
|
+
|
|
5
|
+
# Converts a given OS into a VirtualBox builder
|
|
6
|
+
module PackerFiles
|
|
7
|
+
module Virtual
|
|
8
|
+
class VirtualBoxConverter
|
|
9
|
+
|
|
10
|
+
# Hash Value
|
|
11
|
+
attr_reader :value
|
|
12
|
+
|
|
13
|
+
# Constructor
|
|
14
|
+
def initialize(os, gen)
|
|
15
|
+
@value = convert(os, gen).to_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Convert OS object into a hash
|
|
19
|
+
def convert(os, gen)
|
|
20
|
+
VirtualBox.new do |vbox|
|
|
21
|
+
|
|
22
|
+
# Fill in output directory and builder name
|
|
23
|
+
rel = os.CDImage.release
|
|
24
|
+
arch = os.CDImage.arch
|
|
25
|
+
name = os.name.gsub('::', '-')
|
|
26
|
+
vbox.output_directory = "output-#{name}-#{rel}-#{arch}-#{vbox.type}"
|
|
27
|
+
vbox.name = "#{name}-#{rel}-#{arch}-#{vbox.type}"
|
|
28
|
+
|
|
29
|
+
# Fill in image details, Some amount of url handling
|
|
30
|
+
# is really required to handle various corner cases.
|
|
31
|
+
# thus making the code a bit harder to understand
|
|
32
|
+
vbox.iso_checksum_type = os.CDImage.check_sum_type
|
|
33
|
+
vbox.iso_checksum = os.CDImage.check_sum
|
|
34
|
+
|
|
35
|
+
# The code below creates a user variable called mirror
|
|
36
|
+
# whose base value is the URL fragment w/o the file-name.
|
|
37
|
+
url = URI::parse(os.CDImage.iso_url)
|
|
38
|
+
file = File.basename(url.path)
|
|
39
|
+
url.path = File.dirname(url.path)
|
|
40
|
+
gen.variables['mirror'] = url.to_s
|
|
41
|
+
vbox.iso_url = "{{user `mirror`}}/#{file}"
|
|
42
|
+
|
|
43
|
+
# Fill in SSH specifics
|
|
44
|
+
vbox.ssh_username = os.User.login
|
|
45
|
+
vbox.ssh_password = os.User.password
|
|
46
|
+
vbox.ssh_wait_timeout = '10000s'
|
|
47
|
+
|
|
48
|
+
# Fill in other specifics
|
|
49
|
+
vbox.disk_size = os.Disk.size
|
|
50
|
+
vbox.http_directory = File.basename(os.http_dir)
|
|
51
|
+
vbox.guest_os_type = os.guest_os_type
|
|
52
|
+
|
|
53
|
+
# Fill in boot command and boot_wait timeout
|
|
54
|
+
gen.variables['host'] = ''
|
|
55
|
+
gen.variables['port'] = ''
|
|
56
|
+
user_var_name = name + "-#{vbox.type}-boot-wait"
|
|
57
|
+
gen.variables[user_var_name] = "10s"
|
|
58
|
+
vbox.boot_command = os.boot_command
|
|
59
|
+
vbox.boot_wait = "{{user `#{user_var_name}`}}"
|
|
60
|
+
|
|
61
|
+
# Fill in shutdown command
|
|
62
|
+
vbox.shutdown_command = os.shutdown_command
|
|
63
|
+
|
|
64
|
+
# Generate VBoxManage commands
|
|
65
|
+
vbox.Hypervisor(os.Hypervisors.VirtualBox)
|
|
66
|
+
|
|
67
|
+
# Finally do a Merge with custom
|
|
68
|
+
vbox.merge_hs(os.Hypervisors.VirtualBox.custom)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
# Define Hypervisors for this OS
|
|
3
|
+
<% if @obj.nil? -%>
|
|
4
|
+
#os.<%= @name -%> do |hypervisors|
|
|
5
|
+
<% else -%>
|
|
6
|
+
os.<%= @name -%> do |hypervisors|
|
|
7
|
+
<% end -%>
|
|
8
|
+
|
|
9
|
+
<%
|
|
10
|
+
content = []
|
|
11
|
+
%w[VirtualBox VMWare KVM].each do |func|
|
|
12
|
+
file = @mod.const_get('PackerFiles::Core::Hypervisor').doc_file
|
|
13
|
+
doc = @mod.evaluate_erb(file, {'name' => func})
|
|
14
|
+
content += doc.split("\n").map {|line| ' ' + line }
|
|
15
|
+
end
|
|
16
|
+
-%>
|
|
17
|
+
<%= content.join("\n") -%>
|
|
18
|
+
|
|
19
|
+
<% if @obj.nil? -%>
|
|
20
|
+
#end
|
|
21
|
+
<% else -%>
|
|
22
|
+
end
|
|
23
|
+
<% end -%>
|
data/lib/PackerFiles.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'erubis'
|
|
4
|
+
|
|
5
|
+
# Utility functions for the module
|
|
6
|
+
module PackerFiles
|
|
7
|
+
|
|
8
|
+
# Name of the gem of this module. This is useful elsewhere, but primarily
|
|
9
|
+
# to avoid multiple definitions of gem name everywhere.
|
|
10
|
+
@@gem = 'PackerFiles'
|
|
11
|
+
|
|
12
|
+
# Accessor for the Gem name
|
|
13
|
+
def self.GemName
|
|
14
|
+
@@gem
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Accessor for Gem Spec
|
|
18
|
+
def self.GemSpec
|
|
19
|
+
Gem::Specification.find_by_name(self.GemName)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Given a Path, return it's absolute path within the context of the Gem
|
|
23
|
+
def self.DirPath(file_name)
|
|
24
|
+
self.GemSpec.matches_for_glob(file_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Evaluate a given ERB file in a given OS folder with the object
|
|
28
|
+
# hash. The hash contains object bindings in the form of {var, obj}
|
|
29
|
+
# where {var} is present in the erb file.
|
|
30
|
+
def self.evaluate_erb(filename, hash)
|
|
31
|
+
return Erubis::Eruby.new(File.read(filename)).evaluate(hash)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Top level function that is called by the DSL to generate all the
|
|
35
|
+
# required PackerFiles
|
|
36
|
+
def self.Generate(os_name, version = nil, &block)
|
|
37
|
+
require 'Finder'
|
|
38
|
+
finder = Finder.new
|
|
39
|
+
obj = finder.class_name(os_name, version).new(&block)
|
|
40
|
+
obj.Generate
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
end
|