country.pl: generate final structure as json at build time directly

Currently, we generate a custom-format `country.dat` at build time,
which we then ship with the installer. In the live environment, this
then gets parsed (via regexes) into another format and is finally
written out as JSON for e.g. the TUI and auto-installer to consume.

Instead, skip the intermediate format completely and just generate the
final data structure as JSON at build time.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
This commit is contained in:
Christoph Heiss 2024-12-09 13:45:55 +01:00 committed by Thomas Lamprecht
parent 18a1b8c20d
commit dc401eb3f0
5 changed files with 113 additions and 89 deletions

2
.gitignore vendored
View File

@ -8,4 +8,4 @@
/test*.img /test*.img
/testdir/ /testdir/
Cargo.lock Cargo.lock
country.dat locale-info.json

View File

@ -19,7 +19,7 @@ else
CARGO_COMPILEDIR := target/debug CARGO_COMPILEDIR := target/debug
endif endif
INSTALLER_SOURCES=$(shell git ls-files) country.dat INSTALLER_SOURCES=$(shell git ls-files) locale-info.json
PREFIX = /usr PREFIX = /usr
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
@ -72,9 +72,9 @@ $(BUILDDIR):
cp -a debian $@.tmp/ cp -a debian $@.tmp/
mv $@.tmp $@ mv $@.tmp $@
country.dat: country.pl locale-info.json: country.pl
./country.pl > country.dat.tmp ./country.pl > $@.tmp
mv country.dat.tmp country.dat mv $@.tmp $@
deb: $(DEB) deb: $(DEB)
$(ASSISTANT_DEB): $(DEB) $(ASSISTANT_DEB): $(DEB)
@ -100,10 +100,10 @@ sbuild: $(DSC)
sbuild $(DSC) sbuild $(DSC)
.PHONY: prepare-test-env .PHONY: prepare-test-env
prepare-test-env: cd-info.test country.dat test.img prepare-test-env: cd-info.test locale-info.json test.img
rm -rf testdir rm -rf testdir
mkdir -p testdir/var/lib/proxmox-installer/ mkdir -p testdir/var/lib/proxmox-installer/
cp -v country.dat testdir/var/lib/proxmox-installer/ cp -v locale-info.json testdir/var/lib/proxmox-installer/
./proxmox-low-level-installer -t test.img dump-env ./proxmox-low-level-installer -t test.img dump-env
.PHONY: test .PHONY: test
@ -124,7 +124,7 @@ install: $(INSTALLER_SOURCES) $(COMPILED_BINS)
install -D -m 644 interfaces $(DESTDIR)/etc/network/interfaces install -D -m 644 interfaces $(DESTDIR)/etc/network/interfaces
install -D -m 755 fake-start-stop-daemon $(VARLIBDIR)/fake-start-stop-daemon install -D -m 755 fake-start-stop-daemon $(VARLIBDIR)/fake-start-stop-daemon
install -D -m 755 policy-disable-rc.d $(VARLIBDIR)/policy-disable-rc.d install -D -m 755 policy-disable-rc.d $(VARLIBDIR)/policy-disable-rc.d
install -D -m 644 country.dat $(VARLIBDIR)/country.dat install -D -m 644 locale-info.json $(VARLIBDIR)/locale-info.json
install -D -m 755 unconfigured.sh $(DESTDIR)/sbin/unconfigured.sh install -D -m 755 unconfigured.sh $(DESTDIR)/sbin/unconfigured.sh
install -D -m 755 proxinstall $(DESTDIR)/usr/bin/proxinstall install -D -m 755 proxinstall $(DESTDIR)/usr/bin/proxinstall
install -D -m 755 proxmox-low-level-installer $(DESTDIR)/$(BINDIR)/proxmox-low-level-installer install -D -m 755 proxmox-low-level-installer $(DESTDIR)/$(BINDIR)/proxmox-low-level-installer
@ -226,5 +226,5 @@ check-pbs-tui: prepare-check-pbs
clean: clean:
rm -rf target build $(PACKAGE)-[0-9]* testdir rm -rf target build $(PACKAGE)-[0-9]* testdir
rm -f $(PACKAGE)*.tar* *.deb packages packages.tmp *.build *.dsc *.buildinfo *.changes rm -f $(PACKAGE)*.tar* *.deb packages packages.tmp *.build *.dsc *.buildinfo *.changes
rm -f test*.img pve-final.pkglist country.dat final.pkglist cd-info.test rm -f test*.img pve-final.pkglist locale-info.json final.pkglist cd-info.test
find . -name '*~' -exec rm {} ';' find . -name '*~' -exec rm {} ';'

View File

@ -5,6 +5,9 @@ use warnings;
use Carp; use Carp;
use Cwd (); use Cwd ();
use JSON qw(from_json);
use Proxmox::Sys::File qw(file_read_all);
use base qw(Exporter); use base qw(Exporter);
our @EXPORT = qw(is_test_mode); our @EXPORT = qw(is_test_mode);
@ -33,57 +36,8 @@ my $product_cfg = {
my sub read_locale_info { my sub read_locale_info {
my ($lib_dir) = @_; my ($lib_dir) = @_;
my $countryfn = "${lib_dir}/country.dat"; my $json = file_read_all("${lib_dir}/locale-info.json");
open (my $COUNTRY_MAP_FH, "<:encoding(utf8)", "$countryfn") || die "unable to open '$countryfn' - $!\n"; return from_json($json, { utf8 => 1 });
my ($country, $countryhash, $kmap, $kmaphash) = ({}, {}, {}, {});
while (defined (my $line = <$COUNTRY_MAP_FH>)) {
if ($line =~ m|^map:([^\s:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]*):$|) {
$kmap->{$1} = {
name => $2,
kvm => $3,
console => $4,
x11 => $5,
x11var => $6,
};
$kmaphash->{$2} = $1;
} elsif ($line =~ m|^([a-z]{2}):([^:]+):([^:]*):([^:]*):$|) {
$country->{$1} = {
name => $2,
kmap => $3,
mirror => $4,
};
$countryhash->{lc($2)} = $1;
} else {
warn "unable to parse 'country.dat' line: $line";
}
}
close ($COUNTRY_MAP_FH);
my $zonefn = "/usr/share/zoneinfo/zone.tab";
open (my $ZONE_TAB_FH, '<', "$zonefn") || die "unable to open '$zonefn' - $!\n";
my ($zones, $cczones) = ({}, {});
while (defined (my $line = <$ZONE_TAB_FH>)) {
next if $line =~ m/^\s*(?:#|$)/;
if ($line =~ m|^([A-Z][A-Z])\s+\S+\s+(([^/]+)/\S+)\s|) {
my $cc = lc($1);
$cczones->{$cc}->{$2} = 1;
$country->{$cc}->{zone} = $2 if !defined ($country->{$cc}->{zone});
$zones->{$2} = 1;
}
}
close ($ZONE_TAB_FH);
return {
zones => $zones,
cczones => $cczones,
country => $country,
countryhash => $countryhash,
kmap => $kmap,
kmaphash => $kmaphash,
}
} }
my sub get_cd_info { my sub get_cd_info {

View File

@ -4,32 +4,93 @@ use strict;
use warnings; use warnings;
use PVE::Tools; use PVE::Tools;
use JSON; use JSON qw(from_json to_json);
# Generates a
#
# - country code => name/kmap/mirror
# - name => country code
#
# mapping for each defined country
my sub generate_country_mappings {
my ($country_codes, $defmap, $mirrors) = @_;
my ($countries, $countryhash) = ({}, {});
foreach my $cc (sort keys %$country_codes) {
my $name = $country_codes->{$cc};
my $kmap = $defmap->{$cc} || '';
my $mirror = $mirrors->{$cc} || '';
$countries->{$cc} = {
name => $name,
kmap => $kmap,
mirror => $mirror,
};
$countryhash->{lc($name)} = $cc;
}
return ($countries, $countryhash);
}
# we need mappings for X11, console, and kvm vnc
# LC(-LC)? => [DESC, kvm, console, X11, X11variant]
my sub generate_keymaps {
my ($country_codes) = @_;
my ($kmap, $kmaphash) = ({}, {});
my $keymaps = PVE::Tools::kvmkeymaps();
foreach my $km (sort keys %$keymaps) {
my ($name, $kvm, $console, $x11, $x11var) = @{$keymaps->{$km}};
if ($km =~m/^([a-z][a-z])-([a-z][a-z])$/i) {
defined ($country_codes->{$2}) || die "undefined country code '$2'";
} else {
defined ($country_codes->{$km}) || die "undefined country code '$km'";
}
$x11var = '' if !defined ($x11var);
$kmap->{$km} = {
name => $name,
kvm => $kvm,
console => $console,
x11 => $x11,
x11var => $x11var,
};
$kmaphash->{$name} = $km;
}
return ($kmap, $kmaphash);
}
my sub parse_zoneinfo {
my ($countries) = @_;
my $zonefn = "/usr/share/zoneinfo/zone.tab";
open (my $ZONE_TAB_FH, '<', "$zonefn") || die "unable to open '$zonefn' - $!\n";
my ($zones, $cczones) = ({}, {});
while (defined (my $line = <$ZONE_TAB_FH>)) {
next if $line =~ m/^\s*(?:#|$)/;
if ($line =~ m|^([A-Z][A-Z])\s+\S+\s+(([^/]+)/\S+)\s|) {
my $cc = lc($1);
$cczones->{$cc}->{$2} = 1;
$countries->{$cc}->{zone} = $2 if !defined ($countries->{$cc}->{zone});
$zones->{$2} = 1;
}
}
close ($ZONE_TAB_FH);
return ($zones, $cczones);
}
# country codes from: # country codes from:
my $country_codes_file = "/usr/share/iso-codes/json/iso_3166-1.json"; my $country_codes_file = "/usr/share/iso-codes/json/iso_3166-1.json";
my $iso_3166_codes = from_json(PVE::Tools::file_get_contents($country_codes_file, 64 * 1024)); my $iso_3166_codes = from_json(PVE::Tools::file_get_contents($country_codes_file, 64 * 1024));
my $country = { map { lc($_->{'alpha_2'}) => $_->{'common_name'} // $_->{'name'} } @{$iso_3166_codes->{'3166-1'}} }; my $country_codes = { map { lc($_->{'alpha_2'}) => $_->{'common_name'} // $_->{'name'} } @{$iso_3166_codes->{'3166-1'}} };
# we need mappings for X11, console, and kvm vnc
# LC(-LC)? => [DESC, kvm, console, X11, X11variant]
my $keymaps = PVE::Tools::kvmkeymaps();
foreach my $km (sort keys %$keymaps) {
my ($desc, $kvm, $console, $x11, $x11var) = @{$keymaps->{$km}};
if ($km =~m/^([a-z][a-z])-([a-z][a-z])$/i) {
defined ($country->{$2}) || die "undefined country code '$2'";
} else {
defined ($country->{$km}) || die "undefined country code '$km'";
}
$x11var = '' if !defined ($x11var);
print "map:$km:$desc:$kvm:$console:$x11:$x11var:\n";
}
my $defmap = { my $defmap = {
'us' => 'en-us', 'us' => 'en-us',
@ -37,7 +98,7 @@ my $defmap = {
'br' => 'pt-br', 'br' => 'pt-br',
'ca' => 'en-us', 'ca' => 'en-us',
'dk' => 'dk', 'dk' => 'dk',
'nl' => 'en-us', # most Dutch people us US layout 'nl' => 'en-us', # most Dutch people use US layout
'fi' => 'fi', 'fi' => 'fi',
'fr' => 'fr', 'fr' => 'fr',
'de' => 'de', 'de' => 'de',
@ -61,14 +122,23 @@ my $defmap = {
'li' => 'de-ch', 'li' => 'de-ch',
}; };
my $mirrors = PVE::Tools::debmirrors(); my $mirrors = PVE::Tools::debmirrors();
foreach my $cc (keys %$mirrors) { foreach my $cc (keys %$mirrors) {
die "undefined country code '$cc'" if !defined ($country->{$cc}); die "undefined country code '$cc'" if !defined ($country_codes->{$cc});
} }
foreach my $cc (sort keys %$country) { my ($countries, $countryhash) = generate_country_mappings($country_codes, $defmap, $mirrors);
my $map = $defmap->{$cc} || ''; my ($kmap, $kmaphash) = generate_keymaps($country_codes);
my $mir = $mirrors->{$cc} || ''; my ($zones, $cczones) = parse_zoneinfo($countries);
print "$cc:$country->{$cc}:$map:$mir:\n";
} my $locale_info = {
country => $countries,
countryhash => $countryhash,
kmap => $kmap,
kmaphash => $kmaphash,
zones => $zones,
cczones => $cczones,
};
my $json = to_json($locale_info, { utf8 => 1, canonical => 1 });
print $json;

View File

@ -1 +1 @@
{"iso-info":{"isoname":"proxmox-ve","isorelease":"2","product":"pve","productlong":"Proxmox VE","release":"8.0"},"locations":{"iso":"/cdrom","lib":"/var/lib/proxmox-installer","pkg":"/cdrom/proxmox/packages/","run":"/run/proxmox-installer"},"product":"pve","product-cfg":{"bridged_network":1,"enable_btrfs":1,"fullname":"Proxmox VE","port":"8006","product":"pve"},"run-env-cache-file":"/run/proxmox-installer/run-env-info.json"} {"iso-info":{"isoname":"proxmox-ve","isorelease":"2","product":"pve","productlong":"Proxmox VE","release":"8.0"},"locations":{"iso":"../testdir","lib":"../testdir/var/lib/proxmox-installer","pkg":"../testdir/cdrom/proxmox/packages/","run":"../testdir/run/proxmox-installer"},"product":"pve","product-cfg":{"bridged_network":1,"enable_btrfs":1,"fullname":"Proxmox VE","port":"8006","product":"pve"},"run-env-cache-file":"testdir/run/proxmox-installer/run-env-info.json"}