1 |
95b003ff
|
Origo
|
#!/usr/bin/perl
|
2 |
|
|
|
3 |
|
|
use JSON;
|
4 |
|
|
use URI::Escape::XS qw/uri_escape uri_unescape/;
|
5 |
|
|
use ConfigReader::Simple;
|
6 |
|
|
use Cwd;
|
7 |
|
|
use Data::Dumper;
|
8 |
d3d1a2d4
|
Origo
|
# use Getopt::Std;
|
9 |
|
|
use Getopt::Long qw(GetOptions);
|
10 |
95b003ff
|
Origo
|
|
11 |
d3d1a2d4
|
Origo
|
Getopt::Long::Configure qw(gnu_getopt); # Allow combining short options
|
12 |
95b003ff
|
Origo
|
our %options=();
|
13 |
d3d1a2d4
|
Origo
|
# Getopt::Std::getopts("rapdfsu", \%options);
|
14 |
d24d9a01
|
hq
|
GetOptions(\%options, 'rebuild|r', 'activate|a', 'publish|p', 'debug|d', 'force|f', 'release|s', 'unrelease|u', 'freshen|h');
|
15 |
d3d1a2d4
|
Origo
|
|
16 |
95b003ff
|
Origo
|
my $ofile = $ARGV[(scalar @ARGV)-1] if @ARGV;
|
17 |
d3d1a2d4
|
Origo
|
my $rebuild = 1 if ($options{rebuild});
|
18 |
|
|
my $activate = 1 if ($options{activate});
|
19 |
|
|
my $publish = 1 if ($options{publish});
|
20 |
|
|
my $release = 1 if ($options{release});
|
21 |
|
|
my $debug = 1 if ($options{debug});
|
22 |
|
|
my $force = 1 if ($options{force});
|
23 |
d24d9a01
|
hq
|
my $freshen = 1 if ($options{freshen});
|
24 |
d3d1a2d4
|
Origo
|
my $unrelease = 1 if ($options{unrelease});
|
25 |
48fcda6b
|
Origo
|
$release = 1 if ($unrelease);
|
26 |
95b003ff
|
Origo
|
|
27 |
|
|
my $cwd = cwd();
|
28 |
6fdc8676
|
hq
|
my $gw = `route -n | awk '\$1 == "0.0.0.0" { print \$2 }'`;
|
29 |
|
|
chomp $gw;
|
30 |
95b003ff
|
Origo
|
|
31 |
|
|
unless ($ofile) {
|
32 |
|
|
opendir(DIR, ".");
|
33 |
6fdc8676
|
hq
|
my @files = grep(/\.stack$/,readdir(DIR));
|
34 |
95b003ff
|
Origo
|
closedir(DIR);
|
35 |
|
|
|
36 |
|
|
if (@files) {
|
37 |
|
|
$ofile = $files[0];
|
38 |
|
|
print "Using $ofile as stackfile\n";
|
39 |
|
|
} else {
|
40 |
d3d1a2d4
|
Origo
|
print <<end
|
41 |
|
|
Usage: stackbuilder [-a] [-p] [-r] [-d] [-f] 'stackfile'
|
42 |
|
|
-a, --activate
|
43 |
|
|
-p, --publish
|
44 |
|
|
-r, --rebuild
|
45 |
|
|
-s, --release
|
46 |
|
|
-d, --debug
|
47 |
|
|
-f, --force
|
48 |
|
|
-u, --unrelease
|
49 |
d24d9a01
|
hq
|
-h, --freshen
|
50 |
d3d1a2d4
|
Origo
|
end
|
51 |
|
|
;
|
52 |
|
|
print "To force rebuild, activate and publish in one go use -frap\n";
|
53 |
95b003ff
|
Origo
|
exit;
|
54 |
|
|
}
|
55 |
|
|
}
|
56 |
|
|
unless (-e $ofile) {
|
57 |
|
|
print "Stabile file not found: $ofile\n";
|
58 |
48fcda6b
|
Origo
|
print "Usage: stackbuilder [-a] [-p] [-r] [-d] [-f] 'stackfile'\n-a activate\n-p publish\n-r rebuild\n-s release\n-d debug\n-f force activate\n-u unrelease\n";
|
59 |
|
|
print "To force rebuild, activate, publish and release in one go use -rapsf\n";
|
60 |
95b003ff
|
Origo
|
exit;
|
61 |
|
|
}
|
62 |
|
|
|
63 |
|
|
my $config = ConfigReader::Simple->new($ofile);
|
64 |
|
|
chdir $1 if ($ofile =~ /(.*\/).+/);
|
65 |
|
|
|
66 |
|
|
# The version of the app we are building
|
67 |
|
|
my $version = $config->get("VERSION") || '1.0';
|
68 |
|
|
my $baseimage = $config->get("BASEIMAGE");
|
69 |
|
|
my $rebase = $config->get("REBASE");
|
70 |
|
|
my $basesuite = $config->get("BASESUITE") || 'xenial';
|
71 |
|
|
my $basename = $config->get("BASENAME") || 'ubuntu-16.04';
|
72 |
|
|
$basename = 'ubuntu-18.04' if ($basesuite eq 'bionic');
|
73 |
991e7f1b
|
hq
|
$basename = 'ubuntu-20.04' if ($basesuite eq 'focal');
|
74 |
705b5366
|
hq
|
$basename = 'ubuntu-22.04' if ($basesuite eq 'jammy');
|
75 |
95b003ff
|
Origo
|
my $name = $config->get("NAME");
|
76 |
|
|
die "You must supply a name [NAME]" unless ($name);
|
77 |
|
|
my $appname = $config->get("APPNAME");
|
78 |
|
|
my $dir = $config->get("DIR");
|
79 |
|
|
die "Directory '$dir' [DIR] does not exist" unless (!$dir || -d $dir);
|
80 |
|
|
my $dirtarget = $config->get("DIRTARGET") || '/tmp';
|
81 |
|
|
my $tar = $config->get("TAR");
|
82 |
|
|
my $tartarget = $config->get("TARTARGET") || '/tmp';
|
83 |
705b5366
|
hq
|
my $tarowner = $config->get("TAROWNER");
|
84 |
95b003ff
|
Origo
|
my $git = $config->get("GIT");
|
85 |
|
|
my $gittarget = $config->get("GITTARGET") || '/tmp';
|
86 |
705b5366
|
hq
|
my $gitowner = $config->get("GITOWNER");
|
87 |
95b003ff
|
Origo
|
my $downloadurl = $config->get("DOWNLOADURL");
|
88 |
|
|
my $debs = $config->get("DEBS");
|
89 |
|
|
my $preexec = $config->get("PREEXEC");
|
90 |
|
|
my $postexec = $config->get("POSTEXEC");
|
91 |
|
|
my $service = $config->get("SERVICE");
|
92 |
|
|
my $dname="$name.$version";
|
93 |
|
|
my $size=$config->get("SIZE") || 9216;
|
94 |
|
|
my $masterpath;
|
95 |
|
|
my $managementlink = $config->get("MANAGEMENTLINK") || '';
|
96 |
|
|
my $upgradelink = $config->get("UPGRADELINK") || '';
|
97 |
|
|
my $terminallink = $config->get("TERMINALLINK") || '';
|
98 |
|
|
my $vcpu = $config->get("VCPU") || '';
|
99 |
|
|
my $memory = $config->get("MEMORY") || '';
|
100 |
|
|
my $logo = $config->get("LOGO") || '';
|
101 |
|
|
my $price = $config->get("PRICE") || '';
|
102 |
|
|
my $thumbnail = $config->get("THUMBNAIL") || '';
|
103 |
d24d9a01
|
hq
|
my $ports = $config->get("PORTS") || '';
|
104 |
95b003ff
|
Origo
|
my $summary = $config->get("SUMMARY") || '';
|
105 |
|
|
my $description = $config->get("DESCRIPTION") || '';
|
106 |
|
|
my $appid = $config->get("APPID") || '';
|
107 |
d3d1a2d4
|
Origo
|
my $instances = $config->get("INSTANCES") || '';
|
108 |
95b003ff
|
Origo
|
my $dataimage = $config->get("DATAIMAGE") || '';
|
109 |
54401133
|
hq
|
my $dataimagefs = $config->get("DATAIMAGEFS") || 'ext4';
|
110 |
c899e439
|
Origo
|
my $storagepool = $config->get("STORAGEPOOL") || '';
|
111 |
|
|
my $storagepool2 = $config->get("STORAGEPOOL2") || '';
|
112 |
6fdc8676
|
hq
|
my $datamount = $config->get("DATAMOUNT") || '/mnt/data';
|
113 |
48fcda6b
|
Origo
|
my $fuelpath = ''; # Is set below if we are in a VM
|
114 |
|
|
my $enginelinked = '';
|
115 |
|
|
my $imageuuid = '';
|
116 |
|
|
my $imageuser = '';
|
117 |
95b003ff
|
Origo
|
|
118 |
|
|
# Check if we are on an admin server or on a regular VM
|
119 |
|
|
unless (-e "/mnt/stabile/images" || -e "/stabile-images/images") {
|
120 |
|
|
# Mount shared storage
|
121 |
|
|
`curl --silent http://localhost:10000/stabile/index.cgi?action=mountpools`;
|
122 |
|
|
$fuelpath = `cat /proc/mounts | grep '/mnt/fuel'`;
|
123 |
|
|
die "Unable to mount shared storage" unless ($fuelpath =~ /(\/mnt\/fuel\/pool\d+)/);
|
124 |
|
|
$fuelpath = $1;
|
125 |
6fdc8676
|
hq
|
my $json = `curl --silent -k "https://$gw/stabile/users?action=listids"`;
|
126 |
48fcda6b
|
Origo
|
my $json_obj = from_json($json);
|
127 |
|
|
$enginelinked = 1 if ($json_obj->{items}->[0]->{engine}->{enginelinked});
|
128 |
6fdc8676
|
hq
|
$json = `curl --silent -k "https://$gw/stabile/images?image=$dname.master.qcow2"`;
|
129 |
48fcda6b
|
Origo
|
$json_obj = from_json($json);
|
130 |
|
|
$imageuuid = $json_obj->{uuid} if ($json_obj->{uuid} && $json_obj->{uuid} ne '--');
|
131 |
|
|
$imageuser = $json_obj->{user} if ($json_obj->{user} && $json_obj->{user} ne '--');
|
132 |
95b003ff
|
Origo
|
}
|
133 |
|
|
|
134 |
48fcda6b
|
Origo
|
if (!$rebuild && -e "$cwd/$dname.master.qcow2") { # Master image already exists, try to activate and publish
|
135 |
|
|
print "Found $cwd/$dname.master.qcow2, not building (specify -r to force rebuild)\n";
|
136 |
|
|
} elsif (!$rebuild && $imageuuid) {
|
137 |
|
|
print "$dname.master.qcow2 is already activated on engine by user $imageuser, not building (specify -r to force rebuild)\n";
|
138 |
d24d9a01
|
hq
|
} elsif (!$freshen) {
|
139 |
95b003ff
|
Origo
|
# Load nbd
|
140 |
6fdc8676
|
hq
|
print `killall qemu-nbd`;
|
141 |
95b003ff
|
Origo
|
print `rmmod nbd`;
|
142 |
|
|
print `modprobe nbd max_part=63`;
|
143 |
|
|
|
144 |
|
|
unless (-e "$cwd/$dname.master.qcow2") {
|
145 |
|
|
# If app is based on another image, get a link to it, and mount it
|
146 |
|
|
if ($baseimage) {
|
147 |
|
|
my $basepath = "$cwd/$baseimage";
|
148 |
48fcda6b
|
Origo
|
if ($fuelpath) {
|
149 |
|
|
print ">> Asking engine to link or copy $baseimage\n";
|
150 |
6fdc8676
|
hq
|
print qq|>> curl --silent -k "https://$gw/stabile/images/?action=linkmaster&image=$baseimage"|;
|
151 |
|
|
my $json = `curl --silent -k "https://$gw/stabile/images/?action=linkmaster&image=$baseimage"`;
|
152 |
95b003ff
|
Origo
|
print $json;
|
153 |
|
|
my $jobj = from_json($json);
|
154 |
|
|
my $linkpath = $jobj->{linkpath};
|
155 |
|
|
$basepath = $jobj->{path};
|
156 |
|
|
$masterpath = $jobj->{masterpath};
|
157 |
|
|
unless ($basepath) {
|
158 |
|
|
print ">> No base path received. Perhaps master image is not on fuel storage?\n";
|
159 |
|
|
print $json, "\n";
|
160 |
|
|
exit 0;
|
161 |
|
|
}
|
162 |
|
|
while (!(-e $basepath)) {
|
163 |
|
|
print ">> Waiting for $basepath...\n";
|
164 |
|
|
sleep 1
|
165 |
|
|
}
|
166 |
48fcda6b
|
Origo
|
} elsif (!(-e "$cwd/$baseimage")) {
|
167 |
95b003ff
|
Origo
|
print ">> You are trying to build an image which depends on a baseimage, $baseimage, which is not available\n";
|
168 |
|
|
exit;
|
169 |
|
|
}
|
170 |
|
|
|
171 |
|
|
# Clone base image
|
172 |
|
|
if (-e "$cwd/$dname.master.qcow2") {
|
173 |
|
|
print ">> Destination image already exists: $cwd/$dname.master.qcow2\n";
|
174 |
|
|
} else {
|
175 |
|
|
print `qemu-img create -f qcow2 -b "$basepath" "$cwd/$dname.master.qcow2"`;
|
176 |
|
|
}
|
177 |
|
|
# No baseimage, let's build image from scratch or download
|
178 |
|
|
} else {
|
179 |
|
|
if ($downloadurl) {
|
180 |
|
|
my $dlname = "$cwd/$dname.master.qcow2";
|
181 |
|
|
$dlname = "$cwd/$dname.master.qcow2.tar" if ($downloadurl =~ /\.box$/);
|
182 |
|
|
$cmd = qq|wget -O "$dlname" "$downloadurl"|;
|
183 |
|
|
print `$cmd`;
|
184 |
|
|
if ($dlname =~ /\.tar$/) {
|
185 |
|
|
print ">> untarring...\n";
|
186 |
|
|
print `tar -xvf "$dlname" box.img`;
|
187 |
|
|
print `mv box.img "$cwd/$dname.master.qcow2"`;
|
188 |
|
|
print `rm "$dlname"`;
|
189 |
|
|
}
|
190 |
|
|
} else {
|
191 |
|
|
# die "You need to install python-vm-builder in order to build a base image\n" unless (`which vmbuilder`);
|
192 |
991e7f1b
|
hq
|
die "You need to install virt-builder (provided by libguestfs-tools) in order to build a base image\n" unless (`which virt-builder`);
|
193 |
95b003ff
|
Origo
|
# We unfortunately have to patch vmbuilder
|
194 |
|
|
## See: http://askubuntu.com/questions/819844/kvm-vmbuilder-fails
|
195 |
|
|
|
196 |
|
|
# my $vmbuilder_dapper = "/usr/lib/python2.7/dist-packages/VMBuilder/plugins/ubuntu/dapper.py";
|
197 |
|
|
# if (-e $vmbuilder_dapper) {
|
198 |
|
|
# unless (`grep 'force-confnew' $vmbuilder_dapper`) {
|
199 |
|
|
# print ">> Patching vmbuilder\n";
|
200 |
|
|
# system(qq|perl -pi -e "s/(\'dist-upgrade\')/\'--option=Dpkg::Options::=--force-confnew\', \'dist-upgrade\'/" $vmbuilder_dapper|);
|
201 |
|
|
# unlink($vmbuilder_dapper.'c');
|
202 |
|
|
# }
|
203 |
|
|
# unless (`grep 'force-confnew' $vmbuilder_dapper`) {
|
204 |
|
|
# print ">> Patching vmbuilder\n";
|
205 |
|
|
# system(qq|perl -pi -e "s/(self.install_from_template.*sudoers.*)/# \\\$1/" $vmbuilder_dapper|);
|
206 |
|
|
# unlink($vmbuilder_dapper . 'c');
|
207 |
|
|
# }
|
208 |
|
|
# }
|
209 |
|
|
|
210 |
|
|
#my $cmd = qq|vmbuilder kvm ubuntu -o -v --debug --suite $basesuite --arch amd64 --components main,universe,multiverse --rootsize $size --user stabile --pass stabile --hostname $name --tmpfs 2048 --addpkg linux-image-generic --addpkg wget --addpkg curl --domain stabile.io --ip 10.1.1.2|;
|
211 |
|
|
# print "$cmd\n";
|
212 |
705b5366
|
hq
|
$cmd = qq|virt-builder $basename -o "$cwd/$dname.master.qcow2" --arch x86_64 --size $size --hostname $name --format qcow2|;
|
213 |
95b003ff
|
Origo
|
print "$cmd\n";
|
214 |
|
|
print `$cmd`;
|
215 |
|
|
# The downloaded image is in sparse format - get rid of unused/sparse space
|
216 |
|
|
print "Getting rid of sparse space...\n";
|
217 |
|
|
print `qemu-img convert -O qcow2 "$cwd/$dname.master.qcow2" "$cwd/$dname.master.qcow2.new"`;
|
218 |
|
|
print `mv "$cwd/$dname.master.qcow2.new" "$cwd/$dname.master.qcow2"`;
|
219 |
|
|
# Clean up
|
220 |
|
|
# `mv ubuntu-kvm/*.qcow2 "$cwd/$dname.master.qcow2"`;
|
221 |
|
|
# `rm -r ubuntu-kvm`;
|
222 |
|
|
}
|
223 |
|
|
}
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
# Now load nbd and mount the image
|
227 |
|
|
if (-e "$cwd/$dname.master.qcow2") {
|
228 |
|
|
# Wait for nbd0 to be created
|
229 |
|
|
if (!(-e "/dev/nbd0p1")) {
|
230 |
|
|
print `qemu-nbd -c /dev/nbd0 "$cwd/$dname.master.qcow2"`;
|
231 |
|
|
while (!(-e "/dev/nbd0p1")) {
|
232 |
|
|
print ">> Waiting for nbd0p1...\n";
|
233 |
|
|
sleep 1
|
234 |
|
|
}
|
235 |
|
|
}
|
236 |
|
|
# Mount image
|
237 |
|
|
print `mkdir "/tmp/$dname"` unless (-d "/tmp/$dname");
|
238 |
|
|
my $part; # We assume max 4 partitions and assume the main partition is the last
|
239 |
|
|
for (my $i=4; $i>=0; $i--) {
|
240 |
|
|
if (-e "/dev/nbd0p$i") {
|
241 |
|
|
my $partinfo = `blkid /dev/nbd0p$i`;
|
242 |
|
|
chomp $partinfo;
|
243 |
|
|
if ($partinfo =~ /TYPE="(xfs|ext|.+_member)/) {
|
244 |
|
|
$part = "p$i";
|
245 |
|
|
last;
|
246 |
|
|
}
|
247 |
|
|
}
|
248 |
|
|
}
|
249 |
|
|
print `mount /dev/nbd0$part "/tmp/$dname"` unless (-e "/tmp/$dname/boot");
|
250 |
64c667ea
|
hq
|
# Mount /proc and /dev/pts
|
251 |
95b003ff
|
Origo
|
print `mount --bind /proc "/tmp/$dname/proc"`;
|
252 |
64c667ea
|
hq
|
print `mount --bind /dev/pts "/tmp/$dname/dev/pts"`;
|
253 |
95b003ff
|
Origo
|
# print `mount --bind /dev/pts "/tmp/$dname/dev/pts"`;
|
254 |
|
|
# Make /dev/null available
|
255 |
|
|
# print `mknod -m 666 "/tmp/$dname/dev/null" c 1 3`
|
256 |
6fdc8676
|
hq
|
|
257 |
|
|
# Create data image if specified
|
258 |
|
|
if ($dataimage && !(-e "$cwd/$dname-data.master.qcow2") && (-e "$cwd/$dname.master.qcow2")) {
|
259 |
|
|
print ">> Creating data image $dataimage in $cwd/$dname-data.master.qcow2...\n";
|
260 |
|
|
print `qemu-img create -f qcow2 "$cwd/$dname-data.master.qcow2" $dataimage`;
|
261 |
|
|
# print `modprobe nbd max_part=63`;
|
262 |
|
|
print `qemu-nbd -c /dev/nbd1 "$cwd/$dname-data.master.qcow2"`;
|
263 |
|
|
print `sfdisk /dev/nbd1 << EOF\n;\nEOF`;
|
264 |
afc024ef
|
hq
|
# print `mkfs.ext4 /dev/nbd1p1`;
|
265 |
54401133
|
hq
|
print `mkfs.$dataimagefs /dev/nbd1p1`;
|
266 |
6fdc8676
|
hq
|
print ">> Mounting data image $dataimage on /tmp/$dname$datamount...\n";
|
267 |
|
|
print `mkdir -p "/tmp/$dname$datamount"`;
|
268 |
|
|
print `mount /dev/nbd1p1 "/tmp/$dname$datamount"`;
|
269 |
|
|
|
270 |
|
|
# print `qemu-nbd -d /dev/nbd1`;
|
271 |
|
|
}
|
272 |
|
|
|
273 |
95b003ff
|
Origo
|
} else {
|
274 |
|
|
die "Unable to mount image $cwd/$dname.master.qcow2";
|
275 |
|
|
}
|
276 |
|
|
|
277 |
|
|
# Copy files
|
278 |
|
|
if ($dir) {
|
279 |
|
|
die "'$dir' not found [DIR]" unless (-d $dir);
|
280 |
|
|
print ">> Copying files from $dir to /tmp/$dname$dirtarget...\n";
|
281 |
|
|
print `tar rf "/tmp/$dname.tar" "$dir"`;
|
282 |
|
|
print `tar xf "/tmp/$dname.tar" -C "/tmp/$dname$dirtarget"`;
|
283 |
|
|
print `rm "/tmp/$dname.tar"`;
|
284 |
|
|
}
|
285 |
|
|
|
286 |
|
|
print `rm /tmp/$dname/etc/resolv.conf`;
|
287 |
|
|
print `echo "nameserver 1.1.1.1" > /tmp/$dname/etc/resolv.conf`;
|
288 |
|
|
# Add user stabile
|
289 |
6fdc8676
|
hq
|
print `chroot "/tmp/$dname" useradd stabile -b /home -s /bin/bash 2>/dev/null`;
|
290 |
|
|
print `chroot "/tmp/$dname" mkdir /home/stabile 2>/dev/null`;
|
291 |
48fcda6b
|
Origo
|
print `chroot "/tmp/$dname" chown stabile:stabile /home/stabile`;
|
292 |
95b003ff
|
Origo
|
|
293 |
|
|
# Run pre exec script
|
294 |
|
|
# Stop local webmin from blocking port 10000
|
295 |
|
|
print `systemctl stop webmin`;
|
296 |
|
|
if ($preexec) {
|
297 |
|
|
print "Running pre exec in /tmp/$dname\n";
|
298 |
|
|
foreach my $line (split(/\\n/, $preexec)) { # $preexec may contain a multi-line script
|
299 |
|
|
$line =~ s/^\s+//; # remove leading spaces
|
300 |
|
|
$line =~ s/\s+$//; # remove trailing spaces
|
301 |
|
|
$line =~ s/#.+$//; # remove comments
|
302 |
|
|
$line =~ s/\|/\|chroot "\/tmp\/$dname" /; # redirect pipes
|
303 |
|
|
$line =~ s/\> +/\> \/tmp\/$dname/; # redirect pipes
|
304 |
|
|
$line =~ s/\< +/\> \/tmp\/$dname/; # redirect pipes
|
305 |
|
|
$line =~ s/\$\((.+)\)/\$(chroot "\/tmp\/$dname" $1) /; # handle environment variables
|
306 |
|
|
if ($line) {
|
307 |
|
|
my $cmd = qq|chroot "/tmp/$dname" $line|; # execute command in chroot
|
308 |
|
|
print ">> $cmd\n";
|
309 |
|
|
print `$cmd`;
|
310 |
|
|
}
|
311 |
|
|
}
|
312 |
|
|
}
|
313 |
|
|
|
314 |
|
|
# Install debs
|
315 |
|
|
if ($debs) {
|
316 |
|
|
print ">> Installing packages\n";
|
317 |
d3d1a2d4
|
Origo
|
system(qq|perl -pi -e "s/(deb http.+ bionic-updates universe)/# \\\$1/" /etc/apt/sources.list|);
|
318 |
|
|
system(qq|perl -pi -e "s/(deb http.+ bionic-updates multiverse)/# \\\$1/" /etc/apt/sources.list|);
|
319 |
6fdc8676
|
hq
|
|
320 |
|
|
# Necessary for now to avoid corrupted apt caches
|
321 |
|
|
# print `rm -r /var/lib/apt/lists/*`; # Start from a clean slate
|
322 |
|
|
# print `chroot "/tmp/$dname" dpkg --clear-avail`;
|
323 |
|
|
# print `chroot "/tmp/$dname" sync-available` if (-e "/tmp/$dname/usr/sbin/sync-available");
|
324 |
|
|
|
325 |
95b003ff
|
Origo
|
print `chroot "/tmp/$dname" apt-get update`;
|
326 |
|
|
print `chroot "/tmp/$dname" /bin/bash -c 'DEBIAN_FRONTEND=noninteractive apt-get -q -y --show-progress install $debs'`;
|
327 |
|
|
}
|
328 |
|
|
|
329 |
|
|
# Unpack tar
|
330 |
705b5366
|
hq
|
if ($tar) {
|
331 |
95b003ff
|
Origo
|
print ">> Unpacking files...\n";
|
332 |
705b5366
|
hq
|
if ($tar =~ /^http(s?):\/\//) {
|
333 |
|
|
print "Downloading $tar to /tmp/$dname/tmp\n";
|
334 |
|
|
my $cmd = qq|wget "$tar" --directory-prefix "/tmp/$dname/tmp" 2>&1|;
|
335 |
|
|
print "$cmd\n";
|
336 |
|
|
my $dl = `$cmd`;
|
337 |
64c667ea
|
hq
|
$tar = $1 if ($dl =~ /Saving to: (.+)/);
|
338 |
|
|
$tar = $1 if ($tar =~ /...(\S+).../); # tar now surrounds path with multi-byte chars...
|
339 |
705b5366
|
hq
|
print "Received: $tar\n";
|
340 |
|
|
}
|
341 |
95b003ff
|
Origo
|
if ($tar =~ /\.zip$/) {
|
342 |
705b5366
|
hq
|
print `unzip $tar -d "/tmp/$dname$tartarget" > /dev/null`;
|
343 |
|
|
} elsif ($tar =~ /\.tgz$/ || $tar =~ /\.tar\.gz$/) {
|
344 |
|
|
print `tar zxf "$tar" -C "/tmp/$dname$tartarget"`;
|
345 |
95b003ff
|
Origo
|
} else {
|
346 |
705b5366
|
hq
|
print `tar xf "$tar" -C "/tmp/$dname$tartarget"`;
|
347 |
95b003ff
|
Origo
|
}
|
348 |
705b5366
|
hq
|
`chown -R $tarowner "/tmp/$dname$tartarget"` if ($tarowner);
|
349 |
95b003ff
|
Origo
|
}
|
350 |
|
|
|
351 |
|
|
# Git clone
|
352 |
|
|
if ($git) {
|
353 |
|
|
print ">> Cloning from Git repo...\n";
|
354 |
|
|
print `git clone $git "/tmp/$dname$gittarget"`;
|
355 |
705b5366
|
hq
|
`chown -R $gitowner "/tmp/$dname/$gittarget"` if ($gitowner);
|
356 |
95b003ff
|
Origo
|
}
|
357 |
|
|
|
358 |
|
|
# Run post exec script
|
359 |
|
|
if ($postexec) {
|
360 |
|
|
print "Running post exec in /tmp/$dname\n";
|
361 |
|
|
foreach my $line (split(/\\n/, $postexec)) {
|
362 |
|
|
$line =~ s/^\s+//;
|
363 |
|
|
$line =~ s/\s+$//;
|
364 |
|
|
$line =~ s/#.+$//;
|
365 |
|
|
$line =~ s/\|/\|chroot "\/tmp\/$dname" /;
|
366 |
|
|
$line =~ s/\> +/\> \/tmp\/$dname/;
|
367 |
|
|
$line =~ s/\< +/\> \/tmp\/$dname/;
|
368 |
|
|
$line =~ s/\$\((.+)\)/\$(chroot "\/tmp\/$dname" $1) /;
|
369 |
|
|
if ($line) {
|
370 |
|
|
my $cmd = qq|chroot "/tmp/$dname" $line|;
|
371 |
|
|
print ">> $cmd\n";
|
372 |
|
|
print `$cmd`;
|
373 |
|
|
}
|
374 |
|
|
}
|
375 |
|
|
}
|
376 |
|
|
|
377 |
|
|
# Install boot exec script
|
378 |
|
|
if ($service) {
|
379 |
|
|
my $unit = <<END
|
380 |
|
|
[Unit]
|
381 |
|
|
DefaultDependencies=no
|
382 |
|
|
Description=Stabile $dname
|
383 |
6fdc8676
|
hq
|
After=network-online.target stabile-ubuntu.service webmin.service
|
384 |
|
|
Wants=network-online.target stabile-ubuntu.service webmin.service
|
385 |
95b003ff
|
Origo
|
|
386 |
|
|
[Service]
|
387 |
|
|
Type=oneshot
|
388 |
|
|
ExecStart=$service
|
389 |
6fdc8676
|
hq
|
TimeoutSec=600
|
390 |
95b003ff
|
Origo
|
RemainAfterExit=yes
|
391 |
|
|
|
392 |
|
|
[Install]
|
393 |
|
|
WantedBy=multi-user.target
|
394 |
|
|
END
|
395 |
|
|
;
|
396 |
|
|
`echo "$unit" > "/tmp/$dname/etc/systemd/system/stabile-$dname.service"`;
|
397 |
|
|
`chmod 664 "/tmp/$dname/etc/systemd/system/stabile-$dname.service"`;
|
398 |
|
|
`chmod 755 "/tmp/$dname$service"`;
|
399 |
|
|
`chroot /tmp/$dname ln -s /etc/systemd/system/stabile-$dname.service /etc/systemd/system/multi-user.target.wants/stabile-$dname.service`;
|
400 |
|
|
# `systemctl enable stabile-$dname.service`;
|
401 |
|
|
}
|
402 |
|
|
|
403 |
|
|
if ($debug) {
|
404 |
|
|
print ">> Leaving image mounted on: /tmp/$dname\n";
|
405 |
|
|
print ">> Remember to unmount /tmp/$dname/proc and /tmp/$dname manually and 'killall qemu-nbd' when you are done debugging.\n";
|
406 |
|
|
exit;
|
407 |
|
|
}
|
408 |
|
|
# Start webmin again
|
409 |
|
|
print `killall -9 miniserv.pl`;
|
410 |
|
|
print `systemctl start webmin`;
|
411 |
|
|
sleep 2;
|
412 |
6fdc8676
|
hq
|
# Unmount data image
|
413 |
|
|
if ($dataimage) {
|
414 |
|
|
print `umount "/tmp/$dname$datamount"`;
|
415 |
|
|
print `qemu-nbd -d /dev/nbd1`;
|
416 |
|
|
}
|
417 |
95b003ff
|
Origo
|
# Unmount base image and clean things up
|
418 |
|
|
print `umount "/tmp/$dname/proc"`;
|
419 |
64c667ea
|
hq
|
print `umount "/tmp/$dname/dev/pts"`;
|
420 |
95b003ff
|
Origo
|
print `umount "/tmp/$dname"`;
|
421 |
|
|
print `killall qemu-nbd`;
|
422 |
|
|
print `rm -d "/tmp/$dname"`;
|
423 |
|
|
|
424 |
|
|
# convert to qcow2
|
425 |
|
|
# print "Converting $cwd/$dname.master.qcow2\n";
|
426 |
|
|
# print `qemu-img amend -f qcow2 -o compat=0.10 $cwd/$dname.master.qcow2`;
|
427 |
|
|
|
428 |
|
|
# Rebase image
|
429 |
|
|
if ($rebase) { # Flatten image
|
430 |
2a63870a
|
Christian Orellana
|
sleep 5;
|
431 |
95b003ff
|
Origo
|
print ">> Rebasing and flattening image...\n";
|
432 |
c899e439
|
Origo
|
print `qemu-img rebase --force-share -f qcow2 -b "" "$cwd/$dname.master.qcow2"`;
|
433 |
95b003ff
|
Origo
|
} elsif ($masterpath) { # Update backing file path
|
434 |
2a63870a
|
Christian Orellana
|
sleep 5;
|
435 |
|
|
print ">> Rebasing to new backing file path $masterpath...\n";
|
436 |
|
|
my $res = `qemu-img rebase --force-share -f qcow2 -u -b "$masterpath" "$cwd/$dname.master.qcow2" 2>\&1`;
|
437 |
|
|
if ($res =~ /Failed/) {
|
438 |
|
|
print ">> Trying agin to rebasing to new backing file path $masterpath...\n";
|
439 |
|
|
$res = `qemu-img rebase --force-share -f qcow2 -u -b "$masterpath" "$cwd/$dname.master.qcow2" 2>\&1`;
|
440 |
|
|
print qq|>> Rebasing failed - please rebase manually before activating:\nqemu-img rebase --force-share -f qcow2 -u -b "$masterpath" "$cwd/$dname.master.qcow2"\n| if ($res =~ /failed/i);
|
441 |
|
|
}
|
442 |
6fdc8676
|
hq
|
} else {
|
443 |
|
|
print ">> No master, so not rebasing image...\n";
|
444 |
95b003ff
|
Origo
|
}
|
445 |
|
|
}
|
446 |
|
|
|
447 |
|
|
if ($fuelpath) {
|
448 |
|
|
# Activate and publish image
|
449 |
|
|
$appname = uri_escape($appname);
|
450 |
|
|
$managementlink = uri_escape($managementlink);
|
451 |
|
|
$upgradelink = uri_escape($upgradelink);
|
452 |
|
|
$terminallink = uri_escape($terminallink);
|
453 |
|
|
$logo = uri_escape($logo);
|
454 |
|
|
$thumbnail = uri_escape($thumbnail);
|
455 |
d24d9a01
|
hq
|
$ports = uri_escape($ports);
|
456 |
95b003ff
|
Origo
|
$summary = uri_escape($summary);
|
457 |
|
|
$description = uri_escape($description);
|
458 |
|
|
$price = uri_escape($price);
|
459 |
48fcda6b
|
Origo
|
my $image2 = "$dname-data.master.qcow2" if ($dataimage);
|
460 |
95b003ff
|
Origo
|
|
461 |
|
|
my $imgpath = "$cwd/$dname.master.qcow2";
|
462 |
|
|
# Move image unless already on shared storage
|
463 |
|
|
unless ($imgpath =~ /\/mnt\/fuel\/pool\d+/) {
|
464 |
|
|
die "Unable to move image to shared storage - already exists" if (-e "$fuelpath/$dname.master.qcow2");
|
465 |
|
|
print "Moving $dname.master.qcow2 to $fuelpath\n";
|
466 |
|
|
print `mv -v "$cwd/$dname.master.qcow2" $fuelpath`;
|
467 |
|
|
$imgpath = "$fuelpath/$dname.master.qcow2";
|
468 |
48fcda6b
|
Origo
|
if ($image2) {
|
469 |
|
|
print "Moving $dname-data.master.qcow2 to $fuelpath\n";
|
470 |
|
|
print `mv -v "$cwd/$dname-data.master.qcow2" $fuelpath`;
|
471 |
|
|
}
|
472 |
95b003ff
|
Origo
|
}
|
473 |
|
|
|
474 |
48fcda6b
|
Origo
|
if ($activate) {
|
475 |
|
|
print "Trying to activate stack $dname\n";
|
476 |
|
|
if ($imageuuid && !$force) {
|
477 |
|
|
print "Not activating - this image has already been activated on this engine by user $imageuser. Specify -f if you want to force activation.\n";
|
478 |
|
|
} elsif (!(-e $imgpath)) {
|
479 |
|
|
print "Not activating - image not found. Specify -r if you want to force rebuild it.\n";
|
480 |
|
|
} else {
|
481 |
6fdc8676
|
hq
|
my $cmd = qq|curl --silent -k "https://$gw/stabile/images?action=activate&image=$imgpath&version=$version&name=$appname&managementlink=$managementlink&upgradelink=$upgradelink&terminallink=$terminallink&image2=$image2&force=$force"|;
|
482 |
48fcda6b
|
Origo
|
my $res = `$cmd`;
|
483 |
|
|
print $res;
|
484 |
|
|
$res =~ /(\S{8}-\S{4}-\S{4}-\S{4}-\S{12})$/;
|
485 |
|
|
$imageuuid = $1;
|
486 |
|
|
chomp $imageuuid;
|
487 |
|
|
}
|
488 |
|
|
} elsif ( -e $imgpath) {
|
489 |
|
|
print "Your image has been built - now you should activate it (run stackbuilder again with '-a' option), in order to use it!\n";
|
490 |
|
|
}
|
491 |
|
|
if ($publish) {
|
492 |
|
|
if ($enginelinked) {
|
493 |
|
|
print "Trying to publish stack $dname\n";
|
494 |
|
|
if ($imageuuid) {
|
495 |
|
|
print "Uploading image with uuid $imageuuid to the registry\n";
|
496 |
95b003ff
|
Origo
|
print "Hang on - this may take a while...\n";
|
497 |
6fdc8676
|
hq
|
$res = `curl -k --silent "https://$gw/stabile/images?action=publish&uuid=$imageuuid&appid=$appid&vcpu=$vcpu&memory=$memory&logo=$logo&thumbnail=$thumbnail&price=$price&ports=$ports&summary=$summary&description=$description&image2=$image2&instances=$instances&storagepool=$storagepool&storagepool2=$storagepool2&force=$force"`;
|
498 |
48fcda6b
|
Origo
|
print "$res";
|
499 |
95b003ff
|
Origo
|
} else {
|
500 |
48fcda6b
|
Origo
|
print "Something went wrong, did not get valid uuid $imageuuid - left it in your shared storage.\n";
|
501 |
95b003ff
|
Origo
|
}
|
502 |
48fcda6b
|
Origo
|
} else {
|
503 |
|
|
print "Not publishing - you are not in a VM, or the engine you are running on is not linked with the Registry.\n" if ($publish);
|
504 |
|
|
}
|
505 |
|
|
}
|
506 |
d24d9a01
|
hq
|
if ($freshen) {
|
507 |
|
|
if ($enginelinked) {
|
508 |
|
|
print "Trying to freshen stack $dname\n";
|
509 |
|
|
if ($imageuuid) {
|
510 |
6fdc8676
|
hq
|
$res = `curl -k --silent "https://$gw/stabile/images?action=publish&uuid=$imageuuid&appid=$appid&vcpu=$vcpu&memory=$memory&logo=$logo&thumbnail=$thumbnail&price=$price&ports=$ports&summary=$summary&description=$description&image2=$image2&instances=$instances&storagepool=$storagepool&storagepool2=$storagepool2&force=$force&freshen=$freshen"`;
|
511 |
d24d9a01
|
hq
|
print "$res";
|
512 |
|
|
} else {
|
513 |
|
|
print "Something went wrong, did not freshen the stack.\n";
|
514 |
|
|
}
|
515 |
|
|
} else {
|
516 |
6fdc8676
|
hq
|
print "Not freshening - you are not in a VM, or the engine you are running on is not linked with the Registry.\n";
|
517 |
d24d9a01
|
hq
|
}
|
518 |
|
|
}
|
519 |
48fcda6b
|
Origo
|
if ($release) {
|
520 |
|
|
if ($enginelinked) {
|
521 |
|
|
my $action = ($unrelease)?"unrelease":"release";
|
522 |
|
|
print "Trying to $action stack $dname\n";
|
523 |
|
|
if ($imageuuid) {
|
524 |
|
|
print "Moving image with uuid $imageuuid\n";
|
525 |
6fdc8676
|
hq
|
$res = `curl -k --silent "https://$gw/stabile/images?action=release&uuid=$imageuuid&force=$force&unrelease=$unrelease"`;
|
526 |
48fcda6b
|
Origo
|
print "$res";
|
527 |
|
|
}
|
528 |
|
|
else {
|
529 |
|
|
print "Something went wrong, did not get valid uuid $imageuuid.\n";
|
530 |
|
|
}
|
531 |
|
|
} else {
|
532 |
|
|
print "Not releasing - you are not in a VM, or the engine you are running on is not linked with the Registry.\n" if ($publish);
|
533 |
95b003ff
|
Origo
|
}
|
534 |
|
|
}
|
535 |
|
|
} else {
|
536 |
|
|
print "Looks like we are not running in a VM - leaving image in $cwd/$dname.master.qcow2\n";
|
537 |
|
|
}
|