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,68 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require "Core/CDImage"
|
|
3
|
+
require "Utils/AutoZone"
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
require 'open-uri'
|
|
6
|
+
|
|
7
|
+
# An implementation for CDImage For Fedora
|
|
8
|
+
module PackerFiles
|
|
9
|
+
module Fedora
|
|
10
|
+
class CD < Core::CDImageImpl
|
|
11
|
+
|
|
12
|
+
# Master URL
|
|
13
|
+
@@index = 'https://mirrors.fedoraproject.org/publiclist/Fedora/'
|
|
14
|
+
|
|
15
|
+
# Override method for getting the ISO name
|
|
16
|
+
def get_iso_name_pattern
|
|
17
|
+
'Fedora-@release@-@arch@-DVD.iso'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Override method for getting checksums
|
|
21
|
+
def get_check_sum_pattern
|
|
22
|
+
'Fedora-@release@-@arch@-CHECKSUM'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Override method for checksum type
|
|
26
|
+
def get_check_sum_type
|
|
27
|
+
'sha256'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Override method for getting index URLs. This is a dummy URL
|
|
31
|
+
# which is not accessible.
|
|
32
|
+
def index_urls
|
|
33
|
+
default_urls + mirror_index_urls
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Master URL list for fedora.
|
|
37
|
+
private
|
|
38
|
+
def default_urls
|
|
39
|
+
[
|
|
40
|
+
'https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/@release@/Fedora/@arch@/iso/',
|
|
41
|
+
'https://archives.fedoraproject.org/pub/fedora/linux/releases/@release@/Fedora/@arch@/iso/'
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# List of Mirror URLs for Fedora. We use nokogiri to parse values from the Index URL
|
|
46
|
+
# and try to find a matching mirror for a given country.Since we are doing HTML Parsing
|
|
47
|
+
# the code below is found to fail sometime.
|
|
48
|
+
private
|
|
49
|
+
def mirror_index_urls
|
|
50
|
+
page = Nokogiri::HTML(open(@@index))
|
|
51
|
+
country = Utils::AutoZone.new.JSON['country_code']
|
|
52
|
+
set = page.css('td').select {|td| td.text.end_with?(country)}
|
|
53
|
+
urls = set.map {|node|
|
|
54
|
+
rows = node.parent.css('td[4]/table/tr')
|
|
55
|
+
row = rows.select{|row| row.css('td[1]').text.match('Fedora Linux|Archive')}
|
|
56
|
+
if !row.empty?
|
|
57
|
+
links = row.map {|e| e.css('a').map {|link| link['href']}}.flatten.compact
|
|
58
|
+
links.select {|link| link.start_with?('http')}
|
|
59
|
+
end
|
|
60
|
+
}.flatten
|
|
61
|
+
urls.map {|url|
|
|
62
|
+
url + '/releases/@release@/Fedora/@arch@/iso/'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'OS/CentOS/Server'
|
|
3
|
+
require_relative 'CD'
|
|
4
|
+
|
|
5
|
+
# OS class that handles details for generating Packer files for RHEL
|
|
6
|
+
# Servers.
|
|
7
|
+
module PackerFiles
|
|
8
|
+
module RHEL
|
|
9
|
+
|
|
10
|
+
class Server < PackerFiles::CentOS::Server
|
|
11
|
+
|
|
12
|
+
# Constructor. Yield self for further initialization
|
|
13
|
+
def initialize
|
|
14
|
+
yield self if block_given?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Hook function called before normalizing CD Image
|
|
18
|
+
def cd_image_hook
|
|
19
|
+
self.CDImage.impl = RHEL::CD.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Name of the preseed file
|
|
23
|
+
def kickstart_file
|
|
24
|
+
rel = self.CDImage.release
|
|
25
|
+
arch = self.CDImage.arch
|
|
26
|
+
name = "kickstart-rhel-server-#{rel}-#{arch}.cfg"
|
|
27
|
+
File.join(http_dir, name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Misc. Parameters that are required for installation.
|
|
2
|
+
text
|
|
3
|
+
firstboot --disabled
|
|
4
|
+
reboot
|
|
5
|
+
|
|
6
|
+
# Post Install Script
|
|
7
|
+
%post
|
|
8
|
+
|
|
9
|
+
# enable sudo for first user
|
|
10
|
+
<% str="%#{@obj.User.login} ALL=(ALL) NOPASSWD: ALL" -%>
|
|
11
|
+
echo "<%= str -%>" >> /etc/sudoers.d/<%= @obj.User.login %>
|
|
12
|
+
sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
|
|
13
|
+
|
|
14
|
+
%end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% kickstart = @obj.kickstart_file.gsub(@obj.http_dir,'').gsub('/', '') -%>
|
|
2
|
+
<% if @network.nil? -%>
|
|
3
|
+
<tab> linux ks=http://{{user `host`}}:{{user `port`}}/<%= kickstart -%> <wait>
|
|
4
|
+
<% elsif !@network.is_static? -%>
|
|
5
|
+
<tab> linux ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/<%= kickstart -%> <wait>
|
|
6
|
+
<% else -%>
|
|
7
|
+
<tab> linux ks=http://{{user `host`}}:{{user `port`}}/<%= kickstart -%> <wait>
|
|
8
|
+
<% end -%>
|
|
9
|
+
<% if !@network.nil? && @network.is_static? -%>
|
|
10
|
+
ip=<%= @network.ip %> <wait>
|
|
11
|
+
netmask=<%= @network.mask %> <wait>
|
|
12
|
+
gateway=<%= @network.gateway %> <wait>
|
|
13
|
+
dns=<%= @network.dns.join(" ") %> <wait>
|
|
14
|
+
ksdevice=<%= @network.interface %> <wait>
|
|
15
|
+
<% end -%>
|
|
16
|
+
<enter><wait>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<!DOCTYPE profile>
|
|
3
|
+
<!-- http://doc.opensuse.org/projects/autoyast/configuration.html -->
|
|
4
|
+
<profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
|
|
5
|
+
<general>
|
|
6
|
+
<mode>
|
|
7
|
+
<confirm config:type="boolean">false</confirm>
|
|
8
|
+
<forceboot config:type="boolean">true</forceboot>
|
|
9
|
+
<final_reboot config:type="boolean">false</final_reboot>
|
|
10
|
+
</mode>
|
|
11
|
+
</general>
|
|
12
|
+
<report>
|
|
13
|
+
<messages>
|
|
14
|
+
<show config:type="boolean">false</show>
|
|
15
|
+
<timeout config:type="integer">10</timeout>
|
|
16
|
+
<log config:type="boolean">true</log>
|
|
17
|
+
</messages>
|
|
18
|
+
<warnings>
|
|
19
|
+
<show config:type="boolean">false</show>
|
|
20
|
+
<timeout config:type="integer">10</timeout>
|
|
21
|
+
<log config:type="boolean">true</log>
|
|
22
|
+
</warnings>
|
|
23
|
+
<errors>
|
|
24
|
+
<show config:type="boolean">false</show>
|
|
25
|
+
<timeout config:type="integer">10</timeout>
|
|
26
|
+
<log config:type="boolean">true</log>
|
|
27
|
+
</errors>
|
|
28
|
+
</report>
|
|
29
|
+
<bootloader>
|
|
30
|
+
<loader_type>grub2</loader_type>
|
|
31
|
+
</bootloader>
|
|
32
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
# Case 1: When just a disk is given, always use the regular atomic
|
|
3
|
+
# recipe, unless indicated otherwise.
|
|
4
|
+
-->
|
|
5
|
+
<% if @obj.is_raw? -%>
|
|
6
|
+
|
|
7
|
+
<partitioning config:type="list">
|
|
8
|
+
<drive>
|
|
9
|
+
<initialize config:type="boolean">true</initialize>
|
|
10
|
+
<device><%= @obj.name -%></device>
|
|
11
|
+
<use>all</use>
|
|
12
|
+
</drive>
|
|
13
|
+
</partitioning>
|
|
14
|
+
|
|
15
|
+
<% end -%>
|
|
16
|
+
|
|
17
|
+
<!--
|
|
18
|
+
# Case 2: A disk with multiple partitions
|
|
19
|
+
-->
|
|
20
|
+
<% if @obj.only_partitions? -%>
|
|
21
|
+
<partitioning config:type="list">
|
|
22
|
+
<drive>
|
|
23
|
+
<initialize config:type="boolean">true</initialize>
|
|
24
|
+
<device><%= @obj.name -%></device>
|
|
25
|
+
<use>all</use>
|
|
26
|
+
<partitions config:type="list">
|
|
27
|
+
<% @obj.partitions.each do |part, index| -%>
|
|
28
|
+
<% if part.file_system == 'linux-swap' -%>
|
|
29
|
+
<partition>
|
|
30
|
+
<create config:type="boolean">true</create>
|
|
31
|
+
<partition_nr config:type="integer"><%= index + 1 -%></partition_nr>
|
|
32
|
+
<partition_id config:type="integer">130</partition_id>
|
|
33
|
+
<crypt_fs config:type="boolean">false</crypt_fs>
|
|
34
|
+
<filesystem config:type="symbol">swap</filesystem>
|
|
35
|
+
<format config:type="boolean">true</format>
|
|
36
|
+
<fstopt>defaults</fstopt>
|
|
37
|
+
<loop_fs config:type="boolean">false</loop_fs>
|
|
38
|
+
<mount>swap</mount>
|
|
39
|
+
<resize config:type="boolean">false</resize>
|
|
40
|
+
<size><%= part.size -%></size>
|
|
41
|
+
</partition>
|
|
42
|
+
<% else -%>
|
|
43
|
+
<partition>
|
|
44
|
+
<create config:type="boolean">true</create>
|
|
45
|
+
<partition_nr config:type="integer"><%= index + 1 -%></partition_nr>
|
|
46
|
+
<partition_id config:type="integer">131</partition_id>
|
|
47
|
+
<crypt_fs config:type="boolean">false</crypt_fs>
|
|
48
|
+
<filesystem config:type="symbol"><%= part.file_system -%></filesystem>
|
|
49
|
+
<format config:type="boolean">true</format>
|
|
50
|
+
<fstopt>defaults</fstopt>
|
|
51
|
+
<loop_fs config:type="boolean">false</loop_fs>
|
|
52
|
+
<mount><%= part.mount_point -%></mount>
|
|
53
|
+
<% if part.label.nil? -%>
|
|
54
|
+
<mountby config:type="symbol">device</mountby>
|
|
55
|
+
<% else -%>
|
|
56
|
+
<mountby config:type="symbol">label</mountby>
|
|
57
|
+
<label><%= part.label -%></label>
|
|
58
|
+
<% end -%>
|
|
59
|
+
<resize config:type="boolean">false</resize>
|
|
60
|
+
<size><%= part.size -%></size>
|
|
61
|
+
</partition>
|
|
62
|
+
<% end -%>
|
|
63
|
+
<% end -%>
|
|
64
|
+
</partitions>
|
|
65
|
+
</drive>
|
|
66
|
+
</partitioning>
|
|
67
|
+
<% end -%>
|
|
68
|
+
|
|
69
|
+
<!--
|
|
70
|
+
# Case 3: A disk with Logical Volumes
|
|
71
|
+
-->
|
|
72
|
+
<% if @obj.only_vg? -%>
|
|
73
|
+
<partitioning config:type="list">
|
|
74
|
+
<drive>
|
|
75
|
+
<device><%= @obj.name -%></device>
|
|
76
|
+
<partitions config:type="list">
|
|
77
|
+
<partition>
|
|
78
|
+
<lvm_group><%= @obj.vg.name -%></lvm_group>
|
|
79
|
+
<partition_type>primary</partition_type>
|
|
80
|
+
<size><%= @obj.size -%></size>
|
|
81
|
+
</partition>
|
|
82
|
+
</partitions>
|
|
83
|
+
<use>all</use>
|
|
84
|
+
</drive>
|
|
85
|
+
<drive>
|
|
86
|
+
<device><%= '/dev/' + @obj.name -%></device>
|
|
87
|
+
<is_lvm_vg config:type="boolean">true</is_lvm_vg>
|
|
88
|
+
<partitions config:type="list">
|
|
89
|
+
<% @obj.vg.lvs.each do |lv| -%>
|
|
90
|
+
<partition>
|
|
91
|
+
<% if lv.file_system == 'linux-swap' -%>
|
|
92
|
+
<filesystem config:type="symbol">swap</filesystem>
|
|
93
|
+
<lv_name><%= lv.name -%></lv_name>
|
|
94
|
+
<mount>swap</mount>
|
|
95
|
+
<size><%= lv.size -%></size>
|
|
96
|
+
<% else -%>
|
|
97
|
+
<filesystem config:type="symbol"><%= lv.file_system -%></filesystem>
|
|
98
|
+
<lv_name><%= lv.name -%></lv_name>
|
|
99
|
+
<mount><%= lv.mount_point -%></mount>
|
|
100
|
+
<size><%= lv.size -%></size>
|
|
101
|
+
<% end -%>
|
|
102
|
+
</partition>
|
|
103
|
+
<% end -%>
|
|
104
|
+
</partitions>
|
|
105
|
+
<pesize>4M</pesize>
|
|
106
|
+
<use>all</use>
|
|
107
|
+
</drive>
|
|
108
|
+
</partitioning>
|
|
109
|
+
<% end -%>
|
|
110
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%
|
|
2
|
+
# Remove the subscripts like .UTF-8 from the languages
|
|
3
|
+
language = @obj.default.gsub(/\..*$/, '')
|
|
4
|
+
if @obj.supported.empty?
|
|
5
|
+
languages = default
|
|
6
|
+
else
|
|
7
|
+
list = @obj.supported.map {|s| s.gsub(/\..*$/, '')}
|
|
8
|
+
languages = list.split(' ')
|
|
9
|
+
end
|
|
10
|
+
-%>
|
|
11
|
+
<!--
|
|
12
|
+
Language support
|
|
13
|
+
-->
|
|
14
|
+
<language>
|
|
15
|
+
<language> <%= language -%> </language>
|
|
16
|
+
<languages> <%= languages -%> </languages>
|
|
17
|
+
</language>
|
|
18
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Networking section
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
<!--
|
|
6
|
+
Case 1 Static network with IPV4
|
|
7
|
+
-->
|
|
8
|
+
<% if (@obj.is_static?) && ((@obj.type & PackerFiles::Network::IPV4) > 0) -%>
|
|
9
|
+
|
|
10
|
+
<networking>
|
|
11
|
+
<ipv6 config:type="boolean">false</ipv6>
|
|
12
|
+
<keep_install_network config:type="boolean">true</keep_install_network>
|
|
13
|
+
<dns>
|
|
14
|
+
<dhcp_hostname config:type="boolean">true</dhcp_hostname>
|
|
15
|
+
<dhcp_resolv config:type="boolean">true</dhcp_resolv>
|
|
16
|
+
<domain>local</domain>
|
|
17
|
+
<hostname>linux</hostname>
|
|
18
|
+
</dns>
|
|
19
|
+
<interfaces config:type="list">
|
|
20
|
+
<interface>
|
|
21
|
+
<bootproto>static</bootproto>
|
|
22
|
+
<device><%= @obj.interface -%></device>
|
|
23
|
+
<startmode>onboot</startmode>
|
|
24
|
+
<ipaddr><%= @obj.ip -%></ipaddr>
|
|
25
|
+
<netmask><%= @obj.mask -%></netmask>
|
|
26
|
+
<nameservers config:type="list">
|
|
27
|
+
<% @obj.dns.each do |dns| -%>
|
|
28
|
+
<nameserver><%= dns -%></nameserver>
|
|
29
|
+
<% end -%>
|
|
30
|
+
</nameservers>
|
|
31
|
+
</interface>
|
|
32
|
+
</interfaces>
|
|
33
|
+
<routing>
|
|
34
|
+
<ip_forward config:type="boolean">false</ip_forward>
|
|
35
|
+
<routes config:type="list">
|
|
36
|
+
<route>
|
|
37
|
+
<destination>default</destination>
|
|
38
|
+
<device>-</device>
|
|
39
|
+
<gateway><%= @obj.gateway -%></gateway>
|
|
40
|
+
<netmask>-</netmask>
|
|
41
|
+
</route>
|
|
42
|
+
</routes>
|
|
43
|
+
</routing>
|
|
44
|
+
</networking>
|
|
45
|
+
|
|
46
|
+
<!--
|
|
47
|
+
Case 2 Static network with IPV6
|
|
48
|
+
-->
|
|
49
|
+
<% elsif (@obj.is_static?) && ((@obj.type & PackerFiles::Network::IPV6) > 0) -%>
|
|
50
|
+
|
|
51
|
+
<networking>
|
|
52
|
+
<ipv6 config:type="boolean">true</ipv6>
|
|
53
|
+
<keep_install_network config:type="boolean">true</keep_install_network>
|
|
54
|
+
<dns>
|
|
55
|
+
<dhcp_hostname config:type="boolean">true</dhcp_hostname>
|
|
56
|
+
<dhcp_resolv config:type="boolean">true</dhcp_resolv>
|
|
57
|
+
<domain>local</domain>
|
|
58
|
+
<hostname>linux</hostname>
|
|
59
|
+
</dns>
|
|
60
|
+
<interfaces config:type="list">
|
|
61
|
+
<interface>
|
|
62
|
+
<bootproto>static</bootproto>
|
|
63
|
+
<device><%= @obj.interface -%></device>
|
|
64
|
+
<startmode>onboot</startmode>
|
|
65
|
+
<ipaddr><%= @obj.ip -%></ipaddr>
|
|
66
|
+
<netmask><%= @obj.mask -%></netmask>
|
|
67
|
+
<nameservers config:type="list">
|
|
68
|
+
<% @obj.dns.each do |dns| -%>
|
|
69
|
+
<nameserver><%= dns -%></nameserver>
|
|
70
|
+
<% end -%>
|
|
71
|
+
</nameservers>
|
|
72
|
+
</interface>
|
|
73
|
+
</interfaces>
|
|
74
|
+
<routing>
|
|
75
|
+
<ip_forward config:type="boolean">false</ip_forward>
|
|
76
|
+
<routes config:type="list">
|
|
77
|
+
<route>
|
|
78
|
+
<destination>default</destination>
|
|
79
|
+
<device>-</device>
|
|
80
|
+
<gateway><%= @obj.gateway -%></gateway>
|
|
81
|
+
<netmask>-</netmask>
|
|
82
|
+
</route>
|
|
83
|
+
</routes>
|
|
84
|
+
</routing>
|
|
85
|
+
</networking>
|
|
86
|
+
|
|
87
|
+
<!--
|
|
88
|
+
Case 3 DHCP network with IPV6
|
|
89
|
+
-->
|
|
90
|
+
<% elsif (!@obj.is_static?) && ((@obj.type & PackerFiles::Network::IPV6) > 0) -%>
|
|
91
|
+
|
|
92
|
+
<networking>
|
|
93
|
+
<ipv6 config:type="boolean">true</ipv6>
|
|
94
|
+
<keep_install_network config:type="boolean">true</keep_install_network>
|
|
95
|
+
<dns>
|
|
96
|
+
<dhcp_hostname config:type="boolean">true</dhcp_hostname>
|
|
97
|
+
<dhcp_resolv config:type="boolean">true</dhcp_resolv>
|
|
98
|
+
<domain>local</domain>
|
|
99
|
+
<hostname>linux</hostname>
|
|
100
|
+
</dns>
|
|
101
|
+
<interfaces config:type="list">
|
|
102
|
+
<interface>
|
|
103
|
+
<bootproto>dhcp6</bootproto>
|
|
104
|
+
<device><%= @obj.interface -%></device>
|
|
105
|
+
<startmode>onboot</startmode>
|
|
106
|
+
</interface>
|
|
107
|
+
</interfaces>
|
|
108
|
+
</networking>
|
|
109
|
+
|
|
110
|
+
<!--
|
|
111
|
+
Case 4 DHCP network with IPV4
|
|
112
|
+
-->
|
|
113
|
+
<% elsif (!@obj.is_static?) && ((@obj.type & PackerFiles::Network::IPV4) > 0) -%>
|
|
114
|
+
|
|
115
|
+
<networking>
|
|
116
|
+
<ipv6 config:type="boolean">false</ipv6>
|
|
117
|
+
<keep_install_network config:type="boolean">true</keep_install_network>
|
|
118
|
+
<dns>
|
|
119
|
+
<dhcp_hostname config:type="boolean">true</dhcp_hostname>
|
|
120
|
+
<dhcp_resolv config:type="boolean">true</dhcp_resolv>
|
|
121
|
+
<domain>local</domain>
|
|
122
|
+
<hostname>linux</hostname>
|
|
123
|
+
</dns>
|
|
124
|
+
<interfaces config:type="list">
|
|
125
|
+
<interface>
|
|
126
|
+
<bootproto>dhcp</bootproto>
|
|
127
|
+
<device><%= @obj.interface -%></device>
|
|
128
|
+
<startmode>onboot</startmode>
|
|
129
|
+
</interface>
|
|
130
|
+
</interfaces>
|
|
131
|
+
</networking>
|
|
132
|
+
|
|
133
|
+
<% end -%>
|
|
134
|
+
|
|
135
|
+
<!--
|
|
136
|
+
Firewall section
|
|
137
|
+
-->
|
|
138
|
+
<firewall>
|
|
139
|
+
<enable_firewall config:type="boolean">false</enable_firewall>
|
|
140
|
+
<start_firewall config:type="boolean">false</start_firewall>
|
|
141
|
+
</firewall>
|
|
142
|
+
|
|
143
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Installs Packages and Patterns
|
|
3
|
+
-->
|
|
4
|
+
<%
|
|
5
|
+
# Packages that needs to be added are specified as is. However
|
|
6
|
+
# if a particular package needs to be removed, it can be specified
|
|
7
|
+
# via "-" as the prefix. A Pattern can be specified by enclosing it
|
|
8
|
+
# within a "/ /" a sort of a mnemonic for regular expressions.
|
|
9
|
+
add = @obj.list.select {|a| !a.start_with?('-') && !a.start_with?('/')}
|
|
10
|
+
del = @obj.list.select {|d| d.start_with?('-')}
|
|
11
|
+
pat = @obj.list.select {|p| p.start_with?('/') && p.end_with?('/')}
|
|
12
|
+
-%>
|
|
13
|
+
<software>
|
|
14
|
+
<packages config:type="list">
|
|
15
|
+
<% add.each do |p| -%>
|
|
16
|
+
<package><%= p %-></package>
|
|
17
|
+
<% end -%>
|
|
18
|
+
</packages>
|
|
19
|
+
<patterns config:type="list">
|
|
20
|
+
<% pat.each do |p| -%>
|
|
21
|
+
<pattern><%= p %-></pattern>
|
|
22
|
+
<% end -%>
|
|
23
|
+
</patterns>
|
|
24
|
+
<remove-packages config:type="list">
|
|
25
|
+
<% del.each do |p| -%>
|
|
26
|
+
<package><%= p %-></package>
|
|
27
|
+
<% end -%>
|
|
28
|
+
</remove-packages>
|
|
29
|
+
<software>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
User Configuration Section. Here we need to add non-default groups
|
|
3
|
+
and then add users to those groups.
|
|
4
|
+
-->
|
|
5
|
+
<groups config:type="list">
|
|
6
|
+
<group>
|
|
7
|
+
<gid>100</gid>
|
|
8
|
+
<groupname>users</groupname>
|
|
9
|
+
<userlist/>
|
|
10
|
+
</group>
|
|
11
|
+
<% @obj.groups.each_index do |index|
|
|
12
|
+
gid = 100 + index
|
|
13
|
+
name = @obj.groups[index]
|
|
14
|
+
-%>
|
|
15
|
+
<group>
|
|
16
|
+
<gid><%= gid -%></gid>
|
|
17
|
+
<groupname><%= name -%></groupname>
|
|
18
|
+
<userlist><%= @obj.login -%></userlist>
|
|
19
|
+
</group>
|
|
20
|
+
<% end -%>
|
|
21
|
+
</groups>
|
|
22
|
+
|
|
23
|
+
<user_defaults>
|
|
24
|
+
<expire/>
|
|
25
|
+
<group>100</group>
|
|
26
|
+
<groups/>
|
|
27
|
+
<home>/home</home>
|
|
28
|
+
<inactive>-1</inactive>
|
|
29
|
+
<no_groups config:type="boolean">true</no_groups>
|
|
30
|
+
<shell>/bin/bash</shell>
|
|
31
|
+
<skel>/etc/skel</skel>
|
|
32
|
+
<umask>022</umask>
|
|
33
|
+
</user_defaults>
|
|
34
|
+
|
|
35
|
+
<users config:type="list">
|
|
36
|
+
<user>
|
|
37
|
+
<user_password><%= @obj.password -%></user_password>
|
|
38
|
+
<username>root</username>
|
|
39
|
+
</user>
|
|
40
|
+
<user>
|
|
41
|
+
<fullname><%= @obj.name -%></fullname>
|
|
42
|
+
<gid>100</gid>
|
|
43
|
+
<home><%= '/home' + @obj.login -%></home>
|
|
44
|
+
<password_settings>
|
|
45
|
+
<expire/>
|
|
46
|
+
<flag/>
|
|
47
|
+
<inact>-1</inact>
|
|
48
|
+
<max>99999</max>
|
|
49
|
+
<min>0</min>
|
|
50
|
+
<warn>7</warn>
|
|
51
|
+
</password_settings>
|
|
52
|
+
<shell>/bin/bash</shell>
|
|
53
|
+
<uid><%= @obj.uid -%></uid>
|
|
54
|
+
<user_password><%= @obj.password -%></user_password>
|
|
55
|
+
<username><%= @obj.login -%></username>
|
|
56
|
+
</user>
|
|
57
|
+
</users>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<% yast_xml = @obj.autoyast_file.gsub(@obj.http_dir,'').gsub('/', '') -%>
|
|
2
|
+
<% if @network.nil? -%>
|
|
3
|
+
<esc><enter><wait>
|
|
4
|
+
linux netsetup=dhcp
|
|
5
|
+
install=cd:/<wait>
|
|
6
|
+
lang=en_US
|
|
7
|
+
autoyast=http://{{ .HTTPIP }}:{{ .HTTPPort }}/opensuse-13.1/autoinst.xml<wait>
|
|
8
|
+
textmode=1<wait>
|
|
9
|
+
<enter><wait>
|
|
10
|
+
<tab> text ks=http://{{user `host`}}:{{user `port`}}/<%= kickstart -%> <wait>
|
|
11
|
+
<% elsif !@network.is_static? -%>
|
|
12
|
+
<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/<%= kickstart -%> <wait>
|
|
13
|
+
<% else -%>
|
|
14
|
+
<tab> text ks=http://{{user `host`}}:{{user `port`}}/<%= kickstart -%> <wait>
|
|
15
|
+
<% end -%>
|
|
16
|
+
<% if !@network.nil? && @network.is_static? -%>
|
|
17
|
+
ip=<%= @network.ip %> <wait>
|
|
18
|
+
netmask=<%= @network.mask %> <wait>
|
|
19
|
+
gateway=<%= @network.gateway %> <wait>
|
|
20
|
+
dns=<%= @network.dns.join(" ") %> <wait>
|
|
21
|
+
ksdevice=<%= @network.interface %> <wait>
|
|
22
|
+
<% end -%>
|
|
23
|
+
<enter><wait>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
echo '<%= @obj.User.password -%>' | {{ .Vars }} sudo -E -S sh '{{ .Path }}'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rm -rf /etc/sudoers/<%= @obj.User.login %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
echo "<%= @obj.User.login -%> ALL=NOPASSWD: ALL" >> /etc/sudoers.d/<%= @obj.User.login %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sudo /sbin/halt -h -p
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Requires
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'nokogiri'
|
|
4
|
+
require "Core/CDImage"
|
|
5
|
+
require 'Utils/AutoZone'
|
|
6
|
+
|
|
7
|
+
# An implementation for CDImage which lists all available Ubuntu CD Mirrors
|
|
8
|
+
module PackerFiles
|
|
9
|
+
module Ubuntu
|
|
10
|
+
class CD < Core::CDImageImpl
|
|
11
|
+
|
|
12
|
+
# Index URL which contains all the Ubuntu CD Mirror list
|
|
13
|
+
@@index = 'https://launchpad.net/ubuntu/+cdmirrors'
|
|
14
|
+
|
|
15
|
+
# Override method for getting the ISO name
|
|
16
|
+
def get_iso_name_pattern
|
|
17
|
+
'ubuntu-@release@-server-@arch@.iso'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Override method for getting checksums
|
|
21
|
+
def get_check_sum_pattern
|
|
22
|
+
'MD5SUMS'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Override method for checksum type
|
|
26
|
+
def get_check_sum_type
|
|
27
|
+
'md5'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Override method for getting index URLs. The Master index URLs are
|
|
31
|
+
# always checked first.
|
|
32
|
+
def index_urls
|
|
33
|
+
master_index_urls + mirror_index_urls
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The Index URLs from the main Ubuntu CD Image server.
|
|
37
|
+
private
|
|
38
|
+
def master_index_urls
|
|
39
|
+
%w[
|
|
40
|
+
http://releases.ubuntu.com/@release@/
|
|
41
|
+
]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The Index URLs from other Ubuntu CD mirrors. The CD Mirrors only support CD
|
|
45
|
+
# images and not DVD Images.
|
|
46
|
+
private
|
|
47
|
+
def mirror_index_urls
|
|
48
|
+
|
|
49
|
+
# Use nokogiri to parse the index page, to get all the URLs that contain
|
|
50
|
+
# country name [as detected by AutoZone]. Unfortunately this code can only
|
|
51
|
+
# be understood if HTML structure is visually inspected first.
|
|
52
|
+
page = Nokogiri::HTML(open(@@index))
|
|
53
|
+
country = Utils::AutoZone.new.JSON['country']
|
|
54
|
+
|
|
55
|
+
# Get a list of nodes that match the country name (it is usually just one)
|
|
56
|
+
nodes = page.css('tr.head').select {|n| n.text.include?(country)}
|
|
57
|
+
return [] if nodes.empty?
|
|
58
|
+
|
|
59
|
+
# Looking at the inner text yields the count of mirrors this country has.
|
|
60
|
+
# That value is at array index 5, which maps to index value 4 (from zero)
|
|
61
|
+
count = nodes.first.text.split("\n")[4].to_i
|
|
62
|
+
|
|
63
|
+
# Now we have to walk through next siblings of nodes.first "n" times to
|
|
64
|
+
# get all the mirror URLs
|
|
65
|
+
mirrors = []
|
|
66
|
+
node = nodes.first
|
|
67
|
+
count.times do |i|
|
|
68
|
+
node = node.next_element
|
|
69
|
+
urls = node.css('a').map { |aref|
|
|
70
|
+
aref['href'] if !aref['href'].start_with?('/')
|
|
71
|
+
}
|
|
72
|
+
mirrors << urls
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Compact mirrors to remove nil values and add standard prefixes.
|
|
76
|
+
mirrors.flatten.compact.map {|m| URI::join(m, '@release@/').to_s }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|