public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [RFC] APT repositories API/UI
@ 2021-01-20 10:01 Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC common 1/7] add module for APT Fabian Ebner
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

List the configured repositories and have some basic checks for them.

Much is still missing, but I'd like to get feedback for a few things:

* having distinct API calls for the listing and checking can mean that the list
  of repositories and the list of warnings are not in sync (when the repository
  configuration changes in between calls, which admittedly is unlikely). But we
  could also only have one API call and either always add the warnings or have
  a flag to do so. There are two warnings that are not associated with a
  repository though, and those are for not having the enterprise or neither the
  no-subscription repository configured. But that could be solved by always
  including the enterprise repository, even if it is not defined in a file (in
  that case with 'enabled => 0' of course), and associating those two warnings
  with it.
* This also ties in to how to best display the warnings: currently it's just a
  list at the bottom (will be made more visible of course if that's the approach
  we opt for), but we could also display warnings directly below the repository
  they are for by using the RowBody feature.
* see individual patches


Still missing:
    * warning should also be there if enterprise repository is configured, but
      there is no valid subscription
    * warnings should be more clearly visible
    * button for switching to newer suite to be used before a major upgrade
    * that of course requires a writer for the repository files
    * tests
    * add to PMG
    * re-implement in Rust for PBS


common:

Fabian Ebner (2):
  add module for APT
  APT: add extended repositories check

 src/Makefile   |   1 +
 src/PVE/APT.pm | 398 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 399 insertions(+)
 create mode 100644 src/PVE/APT.pm


manager:

Fabian Ebner (3):
  api: APT: add call to list repositories
  ui: add panel for listing APT repositories
  api: APT: add call for repository check

 PVE/API2/APT.pm             | 115 ++++++++++++++++++++++++++++++++++++
 www/manager6/node/Config.js |   7 +++
 2 files changed, 122 insertions(+)


widget-toolkit:

Fabian Ebner (2):
  add UI for APT repositories
  APT repositories: show list of warnings

 src/Makefile                |   1 +
 src/node/APTRepositories.js | 213 ++++++++++++++++++++++++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 src/node/APTRepositories.js

-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC common 1/7] add module for APT
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC common 2/7] APT: add extended repositories check Fabian Ebner
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Allows parsing the configured APT repositories, both the usual .list files in
one-line format and the newer .sources files in DEB-822 stanza format.

The plan is to add a few more functions, one for checking the configured
repositories and one for the pre-upgrade step of replacing the old suite
name with the newer one.

Also, other functions that currently live in pve-managers's and pmg-api's
API2/APT.pm might make sense to unify and move here.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---

Note that a commented out stanza is not detected even if the commented out
things are a valid repository configuration. Since we plan to also write
repository files in the future such repositories could get lost then. That said,
stanzas can also get disabled by adding an Enabled: false option. I'm not
sure if it's worth trying to detect the commented out ones.

 src/Makefile   |   1 +
 src/PVE/APT.pm | 290 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 291 insertions(+)
 create mode 100644 src/PVE/APT.pm

diff --git a/src/Makefile b/src/Makefile
index 098a648..7e5e3cd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,6 +8,7 @@ PERLDIR=${PREFIX}/share/perl5
 
 LIB_SOURCES = \
 	AtomicFile.pm \
+	APT.pm \
 	Certificate.pm \
 	CLIFormatter.pm \
 	CLIHandler.pm \
diff --git a/src/PVE/APT.pm b/src/PVE/APT.pm
new file mode 100644
index 0000000..75d1810
--- /dev/null
+++ b/src/PVE/APT.pm
@@ -0,0 +1,290 @@
+package PVE::APT;
+
+use strict;
+use warnings;
+
+use PVE::Tools;
+
+my $sources_list_path = '/etc/apt/sources.list';
+my $sources_dir_path = '/etc/apt/sources.list.d';
+
+# see APT's StringToBool() in apt-pkg/contrib/strutl.cc
+my $string_to_bool = sub {
+    my ($string) = @_;
+
+    return if !defined($string);
+
+    $string = lc($string);
+
+    return 0 if $string =~ m/^(0|no|false|without|off|disable)$/;
+    return 1 if $string =~ m/^(1|yes|true|with|on|enable)$/;
+    return;
+};
+
+my $split_list = sub {
+    my ($line) = @_;
+
+    return [] if !defined($line);
+
+    $line =~ s/^\s+//;
+    return [ split(/\s+/, $line) ];
+};
+
+my $consume_key = sub {
+    my ($line) = @_;
+
+    if ($line =~ m/^(\S+?):/) {
+	my ($key, $index) = ($1, $+[0]);
+	$line = substr($line, $index);
+	return ($key, $line);
+    }
+    return (undef, $line);
+};
+
+my $consume_token = sub {
+    my ($line) = @_;
+
+    if ($line =~ m/^\s*(\S+)/) {
+	my ($token, $index) = ($1, $+[0]);
+	$line = substr($line, $index);
+	return ($token, $line);
+    }
+    return (undef, $line);
+};
+
+my $consume_comment = sub {
+    my ($line) = @_;
+
+    my $start = index($line, '#');
+    if ($start != -1) {
+	my $comment = substr($line, $start + 1);
+	$line = substr($line, 0, $start) if $start != -1;
+	return ($comment, $line);
+    }
+    return (undef, $line);
+};
+
+my $consume_options = sub {
+    my ($line) = @_;
+
+    my $options = {};
+
+    if ($line =~ m/^\s*(\[)/) {
+	$line = substr($line, $+[0]);
+
+	my $finished;
+	while ($line !~ m/^\s*\]/ && !$finished) {
+	    (my $option, $line) = $consume_token->($line);
+
+	    $finished = 1 if $option =~ s/]$//;
+
+	    if ($option =~ m/(^[^=]+)=(.+)$/) {
+		$options->{$1} = $2;
+	    } else {
+		die "malformed option '$option'\n";
+	    }
+	}
+	$line =~ s/^\s*\]//;
+    }
+    return ($options, $line);
+};
+
+my $basic_repodata_check = sub {
+    my ($repo) = @_;
+
+    die "missing type(s)\n" if !defined($repo->{Types});
+
+    my $uris = $split_list->($repo->{URIs});
+    die "missing URI(s)\n" if !scalar(@{$uris});
+    foreach my $uri (@{$uris}) {
+	die "invalid URI: '$uri'\n" if $uri !~ m/\S+:\S+/;
+    }
+
+    my $suites = $split_list->($repo->{Suites});
+    my $components = $split_list->($repo->{Components});
+    die "missing suite(s)\n" if !scalar(@{$suites});
+    foreach my $suite (@{$suites}) {
+	if ($suite !~ m|/$| && !scalar(@{$components})) {
+	    die "missing component(s)\n"
+	} elsif ($suite =~ m|/$| && scalar(@{$components})) {
+	    die "absolute suite '$suite' does not allow components\n";
+	}
+    }
+};
+
+my $parse_one_line = sub {
+    my ($line) = @_;
+
+    my $repo = {};
+
+    (my $comment, $line) = $consume_comment->($line);
+    $repo->{comment} = $comment if defined($comment);
+    ($repo->{Types}, $line) = $consume_token->($line);
+    ($repo->{Options}, $line) = $consume_options->($line);
+    ($repo->{URIs}, $line) = $consume_token->($line);
+    ($repo->{Suites}, $line) = $consume_token->($line);
+    $line =~ s/^\s+//;
+    $repo->{Components} = $line if length($line);
+
+    $basic_repodata_check->($repo);
+
+    return $repo;
+};
+
+# .list file with one-line style
+sub parse_list_file {
+    my ($path) = @_;
+
+    my $content = PVE::Tools::file_get_contents($path);
+    my @lines = split(/\n/, $content);
+
+    my $repos = [];
+    my $outer_comment = '';
+
+    my $add_repo = sub {
+	my ($line_number, $enabled, $repo_data) = @_;
+
+	my $full_comment = '';
+	$full_comment = $outer_comment . "\n" if length($outer_comment);
+	$full_comment .= $repo_data->{comment} // '';
+	$repo_data->{comment} = $full_comment if length($full_comment);
+	$outer_comment = '';
+
+	push @{$repos}, {
+	    path => $path,
+	    number => $line_number,
+	    enabled => $enabled,
+	    %{$repo_data},
+	};
+    };
+
+    my $line_number = 0;
+    foreach my $line (@lines) {
+	$line_number++;
+
+	if ($line =~/^\s*$/) {
+	    $outer_comment = '';
+	    next;
+	}
+
+	# determine if it's a commented out/disabled repo or just a comment
+	if ($line =~ /^\s*#/) {
+	    my $commented_out = substr($line, $+[0]);
+	    my $repo_data = eval { $parse_one_line->($commented_out); };
+	    if (my $err = $@) {
+		$outer_comment .= "\n" if length($outer_comment);
+		$outer_comment .= $commented_out;
+	    } else {
+		$add_repo->($line_number, 0, $repo_data);
+	    }
+	    next;
+	}
+
+	my $repo_data = eval { $parse_one_line->($line); };
+	if (my $err = $@) {
+	    die "malformed entry in '$path' line $line_number - $err";
+	} else {
+	    $add_repo->($line_number, 1, $repo_data);
+	}
+    }
+
+    return $repos;
+}
+
+my $parse_stanza_line = sub {
+    my ($line, $repo) = @_;
+
+    (my $key, $line) = $consume_key->($line);
+    die "missing value(s) for option '$key'\n" if $line !~ m/\S+/;
+    $line =~ s/^\s+//;
+
+    if ($key =~ m/^(Types|URIs|Suites|Components)$/) {
+	$repo->{$key} = $line;
+    } else {
+	$repo->{Options}->{$key} = $line;
+    }
+};
+
+my $parse_stanza = sub {
+    my ($stanza) = @_;
+
+    my @lines = split(/\n/, $stanza);
+
+    my $repo = { Options => {} };
+    my $empty = 1; # might be only comments
+    my $comment = '';
+
+    my $line_number = 0;
+    foreach my $line (@lines) {
+	$line_number++;
+
+	if ($line =~ m/^#/) {
+	    my $commented_out = substr($line, $+[0]);
+	    $comment .= "\n" if length($comment);
+	    $comment .= $commented_out;
+	    next;
+	}
+
+	$empty = 0;
+
+	eval { $parse_stanza_line->($line, $repo); };
+	die "line ${line_number} - $@" if $@;
+    }
+
+    return if $empty;
+
+    $repo->{comment} = $comment;
+
+    $basic_repodata_check->($repo);
+
+    return $repo;
+};
+
+# .sources file in DEB822 format
+sub parse_sources_file {
+    my ($path) = @_;
+
+    my $content = PVE::Tools::file_get_contents($path);
+    my @stanzas = split(/\n\s*\n/, $content);
+
+    my $repos = [];
+
+    my $stanza_number = 0;
+    foreach my $stanza (@stanzas) {
+	$stanza_number++;
+
+	my $repo_data = eval { $parse_stanza->($stanza); };
+	if (my $err = $@) {
+	    die "malformed entry in '$path' stanza $stanza_number - $err";
+	} elsif (defined($repo_data)) {
+	    my $enabled = $repo_data->{Options}->{Enabled};
+	    $enabled = $string_to_bool->($enabled) // 1;
+	    push @{$repos}, {
+		path => $path,
+		number => $stanza_number,
+		enabled => $enabled,
+		%{$repo_data},
+	    };
+	}
+    }
+
+    return $repos;
+}
+
+sub list_repositories {
+    my $repos = parse_list_file($sources_list_path);
+    PVE::Tools::dir_glob_foreach($sources_dir_path, '[^.]+\.list', sub {
+	my ($filename) = @_;
+	my $file_repositories = parse_list_file("${sources_dir_path}/${filename}");
+	push @{$repos}, @{$file_repositories};
+    });
+    PVE::Tools::dir_glob_foreach($sources_dir_path, '[^.]+\.sources', sub {
+	my ($filename) = @_;
+	my $file_repositories = parse_sources_file("${sources_dir_path}/${filename}");
+	push @{$repos}, @{$file_repositories};
+    });
+
+    return $repos;
+}
+
+1;
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC common 2/7] APT: add extended repositories check
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC common 1/7] add module for APT Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC manager 3/7] api: APT: add call to list repositories Fabian Ebner
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

To detect old/bad suites and see whether the 'enterprise' repository or at least
the 'no-subscription' repository is configured.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---

Suggestions for further checks are welcome.

Note that the distribution names might conflict for external non-Debian repos
that would re-use Debian names, but I think we can safely ignore that.

 src/PVE/APT.pm | 108 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)

diff --git a/src/PVE/APT.pm b/src/PVE/APT.pm
index 75d1810..9f29593 100644
--- a/src/PVE/APT.pm
+++ b/src/PVE/APT.pm
@@ -287,4 +287,112 @@ sub list_repositories {
     return $repos;
 }
 
+sub check_repositories {
+    my ($repos, $product) = @_;
+
+    my $enterprise_configured = 0;
+    my $no_subscription_configured = 0;
+
+    my $enterprise_uri = "https://enterprise.proxmox.com/debian/${product}";
+    my $enterprise_component = "${product}-enterprise";
+    my $no_subscription_uri = "http://download.proxmox.com/debian/${product}";
+    my $no_subscription_component = "${product}-no-subscription";
+
+    # TODO update for PVE 7.0
+    my @old_suites = (
+	'lenny',
+	'squeeze',
+	'wheezy',
+	'jessie',
+	'stretch',
+	'oldoldstable',
+	'oldstable',
+    );
+
+    my @new_suites = (
+	'unstable',
+	'sid',
+	'experimental',
+    );
+
+    my $warnings = [];
+
+    my $add_warning = sub {
+	my ($repo, $message) = @_;
+
+	if (defined($repo)) {
+	    push @{$warnings}, {
+		path => $repo->{path},
+		number => $repo->{number},
+		message => $message,
+	    };
+	} else {
+	    push @{$warnings}, { message => $message };
+	}
+    };
+
+    my $match_suite = sub {
+	my ($suite, $list) = @_;
+
+	return grep {
+	    $_ =~ m|^\Q$suite\E$| ||
+	    $_ =~ m|^\Q$suite\E-backports$| ||
+	    $_ =~ m|^\Q$suite\E-backports-sloppy$| ||
+	    $_ =~ m|^\Q$suite\E-updates$| ||
+	    $_ =~ m|^\Q$suite\E/updates$|
+	} @{$list};
+    };
+
+    foreach my $repo (@{$repos}) {
+	my $types = $split_list->($repo->{Types});
+	my $uris = $split_list->($repo->{URIs});
+	my $components = $split_list->($repo->{Components});
+	my $suites = $split_list->($repo->{Suites});
+
+	foreach my $type (@{$types}) {
+	    next if $type ne 'deb';
+
+	    foreach my $old_suite (@old_suites) {
+		$add_warning->($repo, "Old suite '${old_suite}' configured!")
+		    if $match_suite->($old_suite, $suites);
+	    }
+
+	    foreach my $new_suite (@new_suites) {
+		$add_warning->($repo, "Suite '${new_suite}' should not be " .
+		    "used in production!") if $match_suite->($new_suite, $suites);
+	    }
+
+	    $add_warning->($repo, "Use the name of the stable distribuition " .
+		"instead of 'stable'!") if $match_suite->('stable', $suites);
+
+	    next if !$repo->{enabled};
+
+	    foreach my $uri (@{$uris}) {
+		if ($uri =~ m|^\Q$enterprise_uri\E/?|) {
+		    foreach my $component (@{$components}) {
+			$enterprise_configured = 1
+			    if $component eq $enterprise_component;
+		    }
+		}
+		if ($uri =~ m|^\Q$no_subscription_uri\E/?|) {
+		    foreach my $component (@{$components}) {
+			$no_subscription_configured = 1
+			    if $component eq $no_subscription_component;
+		    }
+		}
+	    }
+	}
+    }
+
+    if (!$enterprise_configured && !$no_subscription_configured) {
+	$add_warning->(undef, "You should configure either the 'enterprise' " .
+	    "or 'no-subscription' repository!");
+    } elsif (!$enterprise_configured && $no_subscription_configured) {
+	$add_warning->(undef, "The 'no-subscription' repository is not " .
+	    "recommended for production use!");
+    }
+
+    return $warnings;
+}
+
 1;
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC manager 3/7] api: APT: add call to list repositories
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC common 1/7] add module for APT Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC common 2/7] APT: add extended repositories check Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 4/7] add UI for APT repositories Fabian Ebner
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---

There is both metadata (path, number, enabled) and actual data (Suites,
Components, etc.) for a repository. The actual data parts are capitalized.
This makes parsing from the DEB-822 format a bit easier, and as a side effect
distinguishes metadata and actual data. An alternative would be to introduce
nesting in the result like

    {
	path =>
	number =>
	enabled =>
	repodata => {
	    suites =>
	    types =>
	}
    }

but having a flat result makes handling in the UI code easier. Is the current
approach fine or does it need to be uniform or should there be nesting?
Since the UI code should be reused for PBS, this question also affects the
not-yet-written Rust code.

 PVE/API2/APT.pm | 69 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/PVE/API2/APT.pm b/PVE/API2/APT.pm
index fb4954e7..e92770ca 100644
--- a/PVE/API2/APT.pm
+++ b/PVE/API2/APT.pm
@@ -12,6 +12,7 @@ use LWP::UserAgent;
 
 use PVE::pvecfg;
 use PVE::Tools qw(extract_param);
+use PVE::APT;
 use PVE::Cluster;
 use PVE::DataCenterConfig;
 use PVE::SafeSyslog;
@@ -67,6 +68,7 @@ __PACKAGE__->register_method({
 	my $res = [ 
 	    { id => 'changelog' },
 	    { id => 'update' },
+	    { id => 'repositories' },
 	    { id => 'versions' },
 	];
 
@@ -478,6 +480,73 @@ __PACKAGE__->register_method({
 	return $data;
     }});
 
+__PACKAGE__->register_method({
+    name => 'repositories',
+    path => 'repositories',
+    method => 'GET',
+    proxyto => 'node',
+    description => "Get APT repository information.",
+    permissions => {
+	check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	},
+    },
+    returns => {
+	type => "array",
+	description => "List of configured repositories.",
+	items => {
+	    type => "object",
+	    properties => {
+		path => {
+		    type => 'string',
+		    description => "Path to the file that defines the repository.",
+		},
+		number => {
+		    type => 'integer',
+		    description => "Line or stanza number.",
+		},
+		enabled => {
+		    type => 'boolean',
+		},
+		comment => {
+		    type => 'string',
+		    optional => 1,
+		},
+		Types => {
+		    type => 'string',
+		    description => "List of repository types.",
+		},
+		URIs => {
+		    type => 'string',
+		    description => "List of repository URIs.",
+		},
+		Suites => {
+		    type => 'string',
+		    description => "List of repository suites.",
+		},
+		Components => {
+		    type => 'string',
+		    description => "List of repository components. Needs to " .
+			"be empty when the suite is absolute.",
+		    optional => 1,
+		},
+		Options => {
+		    type => 'object',
+		    description => "Further options for APT.",
+		},
+	    },
+	},
+    },
+    code => sub {
+	my ($param) = @_;
+
+	return PVE::APT::list_repositories();
+    }});
+
 __PACKAGE__->register_method({
     name => 'versions', 
     path => 'versions', 
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC widget-toolkit 4/7] add UI for APT repositories
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
                   ` (2 preceding siblings ...)
  2021-01-20 10:01 ` [pve-devel] [RFC manager 3/7] api: APT: add call to list repositories Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC manager 5/7] ui: add panel for listing " Fabian Ebner
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---
 src/Makefile                |   1 +
 src/node/APTRepositories.js | 131 ++++++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+)
 create mode 100644 src/node/APTRepositories.js

diff --git a/src/Makefile b/src/Makefile
index fbc2627..b2e9495 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -54,6 +54,7 @@ JSSRC=					\
 	window/DiskSmart.js		\
 	window/ZFSDetail.js		\
 	node/APT.js			\
+	node/APTRepositories.js		\
 	node/NetworkEdit.js		\
 	node/NetworkView.js		\
 	node/DNSEdit.js			\
diff --git a/src/node/APTRepositories.js b/src/node/APTRepositories.js
new file mode 100644
index 0000000..5982306
--- /dev/null
+++ b/src/node/APTRepositories.js
@@ -0,0 +1,131 @@
+Ext.define('apt-repolist', {
+    extend: 'Ext.data.Model',
+    fields: [
+	'path',
+	'number',
+	{
+	    name: "filetype",
+	    convert: function(value, record) {
+		let path = record.data.path;
+		if (value || !path) {
+		    return value;
+		}
+		let match = path.match(/^.+\.(.+)/);
+		if (!match) {
+		    throw "got invalid path";
+		}
+		return match[1];
+	    },
+	},
+	'enabled',
+	'comment',
+	'Types',
+	'URIs',
+	'Suites',
+	'Components',
+	'Options',
+    ],
+});
+
+Ext.define('Proxmox.node.APTRepositories', {
+    extend: 'Ext.grid.GridPanel',
+
+    xtype: 'proxmoxNodeAPTRepositories',
+
+    sortableColumns: false,
+
+    columns: [
+	{
+	    header: gettext('Enabled'),
+	    dataIndex: 'enabled',
+	    renderer: Proxmox.Utils.format_enabled_toggle,
+	    width: 90,
+	},
+	{
+	    header: gettext('Types'),
+	    dataIndex: 'Types',
+	    width: 100,
+	},
+	{
+	    header: gettext('URIs'),
+	    dataIndex: 'URIs',
+	    width: 350,
+	},
+	{
+	    header: gettext('Suites'),
+	    dataIndex: 'Suites',
+	    width: 130,
+	},
+	{
+	    header: gettext('Components'),
+	    dataIndex: 'Components',
+	    width: 170,
+	},
+	{
+	    header: gettext('Options'),
+	    dataIndex: 'Options',
+	    renderer: function(options, cell, record) {
+		let filetype = record.data.filetype;
+		let text = '';
+		for (let [key, value] of Object.entries(options)) {
+		    if (filetype === 'list') {
+			text += `${key}=${value} `;
+		    } else if (filetype === 'sources') {
+			text += `${key}: ${value}\n`;
+		    } else {
+			throw "unkown file type";
+		    }
+		}
+		return text;
+	    },
+	    flex: 1,
+	},
+	{
+	    header: gettext('Comment'),
+	    dataIndex: 'comment',
+	    flex: 1,
+	},
+    ],
+
+    initComponent: function() {
+	let me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+
+	let store = Ext.create('Ext.data.Store', {
+	    model: 'apt-repolist',
+	    groupField: 'path',
+	    proxy: {
+		type: 'proxmox',
+		url: "/api2/json/nodes/" + me.nodename + "/apt/repositories",
+	    },
+	    sorters: [
+		{
+		    property: 'number',
+		    direction: 'ASC',
+		},
+	    ],
+	});
+
+	let groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
+	    groupHeaderTpl: '{[ "File: " + values.name ]} ({rows.length} ' +
+		'repositor{[values.rows.length > 1 ? "ies" : "y"]})',
+	    enableGroupingMenu: false,
+	});
+
+	let sm = Ext.create('Ext.selection.RowModel', {});
+
+	Ext.apply(me, {
+	    store: store,
+	    selModel: sm,
+	    features: [groupingFeature],
+	});
+
+	Proxmox.Utils.monStoreErrors(me, store, true);
+	store.load();
+
+	me.callParent();
+    },
+});
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC manager 5/7] ui: add panel for listing APT repositories
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
                   ` (3 preceding siblings ...)
  2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 4/7] add UI for APT repositories Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC manager 6/7] api: APT: add call for repository check Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 7/7] APT repositories: show list of warnings Fabian Ebner
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---
 www/manager6/node/Config.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js
index ef3ac32c..a42e8644 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -247,6 +247,13 @@ Ext.define('PVE.node.Config', {
 
 	if (caps.nodes['Sys.Audit']) {
 	    me.items.push(
+		{
+		    xtype: 'proxmoxNodeAPTRepositories',
+		    title: gettext('APT Repositories'),
+		    iconCls: 'fa fa-files-o',
+		    itemId: 'aptrepositories',
+		    nodename: nodename,
+		},
 		{
 		    xtype: 'pveFirewallRules',
 		    iconCls: 'fa fa-shield',
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC manager 6/7] api: APT: add call for repository check
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
                   ` (4 preceding siblings ...)
  2021-01-20 10:01 ` [pve-devel] [RFC manager 5/7] ui: add panel for listing " Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 7/7] APT repositories: show list of warnings Fabian Ebner
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---
 PVE/API2/APT.pm | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/PVE/API2/APT.pm b/PVE/API2/APT.pm
index e92770ca..aa3d9b94 100644
--- a/PVE/API2/APT.pm
+++ b/PVE/API2/APT.pm
@@ -69,6 +69,7 @@ __PACKAGE__->register_method({
 	    { id => 'changelog' },
 	    { id => 'update' },
 	    { id => 'repositories' },
+	    { id => 'repositoriescheck' },
 	    { id => 'versions' },
 	];
 
@@ -547,6 +548,51 @@ __PACKAGE__->register_method({
 	return PVE::APT::list_repositories();
     }});
 
+__PACKAGE__->register_method({
+    name => 'repositoriescheck',
+    path => 'repositoriescheck',
+    method => 'GET',
+    proxyto => 'node',
+    description => "Check APT repositories.",
+    permissions => {
+	check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	},
+    },
+    returns => {
+	type => "array",
+	description => "List of warnings.",
+	items => {
+	    type => "object",
+	    properties => {
+		path => {
+		    type => 'string',
+		    description => "Path to the file that defines the repository.",
+		    optional => 1,
+		},
+		number => {
+		    type => 'integer',
+		    description => "Line or stanza number.",
+		    optional => 1,
+		},
+		message => {
+		    type => 'string',
+		    description => "The warning message.",
+		},
+	    },
+	},
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $repos = PVE::APT::list_repositories();
+	return PVE::APT::check_repositories($repos, 'pve');
+    }});
+
 __PACKAGE__->register_method({
     name => 'versions', 
     path => 'versions', 
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

* [pve-devel] [RFC widget-toolkit 7/7] APT repositories: show list of warnings
  2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
                   ` (5 preceding siblings ...)
  2021-01-20 10:01 ` [pve-devel] [RFC manager 6/7] api: APT: add call for repository check Fabian Ebner
@ 2021-01-20 10:01 ` Fabian Ebner
  6 siblings, 0 replies; 8+ messages in thread
From: Fabian Ebner @ 2021-01-20 10:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---
 src/node/APTRepositories.js | 88 +++++++++++++++++++++++++++++++++++--
 1 file changed, 85 insertions(+), 3 deletions(-)

diff --git a/src/node/APTRepositories.js b/src/node/APTRepositories.js
index 5982306..2c46af5 100644
--- a/src/node/APTRepositories.js
+++ b/src/node/APTRepositories.js
@@ -1,3 +1,25 @@
+Ext.define('Proxmox.node.APTRepositories', {
+    extend: 'Ext.container.Container',
+    xtype: 'proxmoxNodeAPTRepositories',
+
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    items: [
+	{
+	    xtype: 'proxmoxNodeAPTRepositoriesMain',
+	    cbind: {
+		nodename: '{nodename}',
+	    },
+	},
+	{
+	    xtype: 'proxmoxNodeAPTRepositoriesWarnings',
+	    cbind: {
+		nodename: '{nodename}',
+	    },
+	},
+    ],
+});
+
 Ext.define('apt-repolist', {
     extend: 'Ext.data.Model',
     fields: [
@@ -27,10 +49,9 @@ Ext.define('apt-repolist', {
     ],
 });
 
-Ext.define('Proxmox.node.APTRepositories', {
+Ext.define('Proxmox.node.APTRepositoriesMain', {
     extend: 'Ext.grid.GridPanel',
-
-    xtype: 'proxmoxNodeAPTRepositories',
+    xtype: 'proxmoxNodeAPTRepositoriesMain',
 
     sortableColumns: false,
 
@@ -129,3 +150,64 @@ Ext.define('Proxmox.node.APTRepositories', {
 	me.callParent();
     },
 });
+
+Ext.define('apt-repolist-warnings', {
+    extend: 'Ext.data.Model',
+    fields: [
+	{
+	    name: "location",
+	    convert: function(value, record) {
+		let path = record.data.path;
+		if (value || !path) {
+		    return value;
+		}
+		let number = record.data.number || '';
+		return path + ':' + number;
+	    },
+	},
+	'message',
+    ],
+});
+
+Ext.define('Proxmox.node.APTRepositoriesWarnings', {
+    extend: 'Ext.grid.GridPanel',
+    xtype: 'proxmoxNodeAPTRepositoriesWarnings',
+
+    columns: [
+	{
+	    header: "Warning",
+	    dataIndex: "message",
+	    flex: 1,
+	},
+	{
+	    header: "Location",
+	    dataIndex: "location",
+	    flex: 1,
+	},
+    ],
+
+    initComponent: function() {
+	let me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+
+	let store = Ext.create('Ext.data.Store', {
+	    model: 'apt-repolist-warnings',
+	    proxy: {
+		type: 'proxmox',
+		url: "/api2/json/nodes/" + me.nodename + "/apt/repositoriescheck",
+	    },
+	});
+
+	Ext.apply(me, {
+	    store: store,
+	});
+
+	Proxmox.Utils.monStoreErrors(me, store, true);
+	store.load();
+
+	me.callParent();
+    },
+});
-- 
2.20.1





^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-01-20 10:02 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-20 10:01 [pve-devel] [RFC] APT repositories API/UI Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC common 1/7] add module for APT Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC common 2/7] APT: add extended repositories check Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC manager 3/7] api: APT: add call to list repositories Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 4/7] add UI for APT repositories Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC manager 5/7] ui: add panel for listing " Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC manager 6/7] api: APT: add call for repository check Fabian Ebner
2021-01-20 10:01 ` [pve-devel] [RFC widget-toolkit 7/7] APT repositories: show list of warnings Fabian Ebner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal