1
|
#!/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
|
# use Getopt::Std;
|
9
|
use Getopt::Long qw(GetOptions);
|
10
|
|
11
|
Getopt::Long::Configure qw(gnu_getopt); # Allow combining short options
|
12
|
our %options=();
|
13
|
# Getopt::Std::getopts("rapdfsu", \%options);
|
14
|
GetOptions(\%options, 'rebuild|r', 'activate|a', 'publish|p', 'debug|d', 'force|f', 'release|s', 'unrelease|u', 'freshen|h');
|
15
|
|
16
|
my $ofile = $ARGV[(scalar @ARGV)-1] if @ARGV;
|
17
|
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
|
my $freshen = 1 if ($options{freshen});
|
24
|
my $unrelease = 1 if ($options{unrelease});
|
25
|
$release = 1 if ($unrelease);
|
26
|
|
27
|
my $cwd = cwd();
|
28
|
my $gw = `route -n | awk '\$1 == "0.0.0.0" { print \$2 }'`;
|
29
|
chomp $gw;
|
30
|
|
31
|
unless ($ofile) {
|
32
|
opendir(DIR, ".");
|
33
|
my @files = grep(/\.stack$/,readdir(DIR));
|
34
|
closedir(DIR);
|
35
|
|
36
|
if (@files) {
|
37
|
$ofile = $files[0];
|
38
|
print "Using $ofile as stackfile\n";
|
39
|
} else {
|
40
|
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
|
-h, --freshen
|
50
|
end
|
51
|
;
|
52
|
print "To force rebuild, activate and publish in one go use -frap\n";
|
53
|
exit;
|
54
|
}
|
55
|
}
|
56
|
unless (-e $ofile) {
|
57
|
print "Stabile file not found: $ofile\n";
|
58
|
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
|
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
|
$basename = 'ubuntu-20.04' if ($basesuite eq 'focal');
|
74
|
$basename = 'ubuntu-22.04' if ($basesuite eq 'jammy');
|
75
|
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
|
my $tarowner = $config->get("TAROWNER");
|
84
|
my $git = $config->get("GIT");
|
85
|
my $gittarget = $config->get("GITTARGET") || '/tmp';
|
86
|
my $gitowner = $config->get("GITOWNER");
|
87
|
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
|
my $ports = $config->get("PORTS") || '';
|
104
|
my $summary = $config->get("SUMMARY") || '';
|
105
|
my $description = $config->get("DESCRIPTION") || '';
|
106
|
my $appid = $config->get("APPID") || '';
|
107
|
my $instances = $config->get("INSTANCES") || '';
|
108
|
my $dataimage = $config->get("DATAIMAGE") || '';
|
109
|
my $dataimagefs = $config->get("DATAIMAGEFS") || 'ext4';
|
110
|
my $storagepool = $config->get("STORAGEPOOL") || '';
|
111
|
my $storagepool2 = $config->get("STORAGEPOOL2") || '';
|
112
|
my $datamount = $config->get("DATAMOUNT") || '/mnt/data';
|
113
|
my $fuelpath = ''; # Is set below if we are in a VM
|
114
|
my $enginelinked = '';
|
115
|
my $imageuuid = '';
|
116
|
my $imageuser = '';
|
117
|
|
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
|
my $json = `curl --silent -k "https://$gw/stabile/users?action=listids"`;
|
126
|
my $json_obj = from_json($json);
|
127
|
$enginelinked = 1 if ($json_obj->{items}->[0]->{engine}->{enginelinked});
|
128
|
$json = `curl --silent -k "https://$gw/stabile/images?image=$dname.master.qcow2"`;
|
129
|
$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
|
}
|
133
|
|
134
|
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
|
} elsif (!$freshen) {
|
139
|
# Load nbd
|
140
|
print `killall qemu-nbd`;
|
141
|
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
|
if ($fuelpath) {
|
149
|
print ">> Asking engine to link or copy $baseimage\n";
|
150
|
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
|
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
|
} elsif (!(-e "$cwd/$baseimage")) {
|
167
|
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
|
die "You need to install virt-builder (provided by libguestfs-tools) in order to build a base image\n" unless (`which virt-builder`);
|
193
|
# 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
|
$cmd = qq|virt-builder $basename -o "$cwd/$dname.master.qcow2" --arch x86_64 --size $size --hostname $name --format qcow2|;
|
213
|
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
|
# Mount /proc and /dev/pts
|
251
|
print `mount --bind /proc "/tmp/$dname/proc"`;
|
252
|
print `mount --bind /dev/pts "/tmp/$dname/dev/pts"`;
|
253
|
# 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
|
|
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
|
# print `mkfs.ext4 /dev/nbd1p1`;
|
265
|
print `mkfs.$dataimagefs /dev/nbd1p1`;
|
266
|
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
|
} 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
|
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
|
print `chroot "/tmp/$dname" chown stabile:stabile /home/stabile`;
|
292
|
|
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
|
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
|
|
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
|
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
|
if ($tar) {
|
331
|
print ">> Unpacking files...\n";
|
332
|
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
|
$tar = $1 if ($dl =~ /Saving to: (.+)/);
|
338
|
$tar = $1 if ($tar =~ /...(\S+).../); # tar now surrounds path with multi-byte chars...
|
339
|
print "Received: $tar\n";
|
340
|
}
|
341
|
if ($tar =~ /\.zip$/) {
|
342
|
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
|
} else {
|
346
|
print `tar xf "$tar" -C "/tmp/$dname$tartarget"`;
|
347
|
}
|
348
|
`chown -R $tarowner "/tmp/$dname$tartarget"` if ($tarowner);
|
349
|
}
|
350
|
|
351
|
# Git clone
|
352
|
if ($git) {
|
353
|
print ">> Cloning from Git repo...\n";
|
354
|
print `git clone $git "/tmp/$dname$gittarget"`;
|
355
|
`chown -R $gitowner "/tmp/$dname/$gittarget"` if ($gitowner);
|
356
|
}
|
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
|
After=network-online.target stabile-ubuntu.service webmin.service
|
384
|
Wants=network-online.target stabile-ubuntu.service webmin.service
|
385
|
|
386
|
[Service]
|
387
|
Type=oneshot
|
388
|
ExecStart=$service
|
389
|
TimeoutSec=600
|
390
|
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
|
# Unmount data image
|
413
|
if ($dataimage) {
|
414
|
print `umount "/tmp/$dname$datamount"`;
|
415
|
print `qemu-nbd -d /dev/nbd1`;
|
416
|
}
|
417
|
# Unmount base image and clean things up
|
418
|
print `umount "/tmp/$dname/proc"`;
|
419
|
print `umount "/tmp/$dname/dev/pts"`;
|
420
|
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
|
sleep 5;
|
431
|
print ">> Rebasing and flattening image...\n";
|
432
|
print `qemu-img rebase --force-share -f qcow2 -b "" "$cwd/$dname.master.qcow2"`;
|
433
|
} elsif ($masterpath) { # Update backing file path
|
434
|
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
|
} else {
|
443
|
print ">> No master, so not rebasing image...\n";
|
444
|
}
|
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
|
$ports = uri_escape($ports);
|
456
|
$summary = uri_escape($summary);
|
457
|
$description = uri_escape($description);
|
458
|
$price = uri_escape($price);
|
459
|
my $image2 = "$dname-data.master.qcow2" if ($dataimage);
|
460
|
|
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
|
if ($image2) {
|
469
|
print "Moving $dname-data.master.qcow2 to $fuelpath\n";
|
470
|
print `mv -v "$cwd/$dname-data.master.qcow2" $fuelpath`;
|
471
|
}
|
472
|
}
|
473
|
|
474
|
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
|
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
|
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
|
print "Hang on - this may take a while...\n";
|
497
|
$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
|
print "$res";
|
499
|
} else {
|
500
|
print "Something went wrong, did not get valid uuid $imageuuid - left it in your shared storage.\n";
|
501
|
}
|
502
|
} 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
|
if ($freshen) {
|
507
|
if ($enginelinked) {
|
508
|
print "Trying to freshen stack $dname\n";
|
509
|
if ($imageuuid) {
|
510
|
$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
|
print "$res";
|
512
|
} else {
|
513
|
print "Something went wrong, did not freshen the stack.\n";
|
514
|
}
|
515
|
} else {
|
516
|
print "Not freshening - you are not in a VM, or the engine you are running on is not linked with the Registry.\n";
|
517
|
}
|
518
|
}
|
519
|
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
|
$res = `curl -k --silent "https://$gw/stabile/images?action=release&uuid=$imageuuid&force=$force&unrelease=$unrelease"`;
|
526
|
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
|
}
|
534
|
}
|
535
|
} else {
|
536
|
print "Looks like we are not running in a VM - leaving image in $cwd/$dname.master.qcow2\n";
|
537
|
}
|