+sub validate_subscription(@) {
+ # Using the API parameter names
+ my $targetUrl = $_[0] || "";
+ my $eventType = $_[1] || "";
+ my $maxRetries = $_[2] || "";
+ my $retryTimer = $_[3] || "";
+ my $retval = 1;
+
+ if (! ($targetUrl =~ /^http:\/\/.*/ or $targetUrl =~ /^https:\/\/.*/)) {
+ print "$myname: bad URL $targetUrl\n";
+ $retval = 0;
+ }
+ if ($eventType ne "created" and $eventType ne "deleted" and
+ $eventType ne "all") {
+ print "$myname: unrecognized event $eventType\n";
+ $retval = 0;
+ }
+ if (! ($maxRetries =~ /^[0-9]+$/)) {
+ print "$myname: invalid maximum retries count $maxRetries\n";
+ $retval = 0;
+ }
+ if (! ($retryTimer =~ /^[0-9]+$/)) {
+ print "$myname: invalid retry time $retryTimer\n";
+ $retval = 0;
+ }
+ return $retval;
+}
+
+# Format a subscriptionRequest JSON object
+
+sub make_subscriptionRequest(@) {
+ my $targetUrl = $_[0];
+ my $eventType = $_[1];
+ my $maxRetries = $_[2];
+ my $retryTimer = $_[3];
+ return "{\"Data\": {\"TargetUrl\":\"$targetUrl\",\"EventType\":\"$eventType\",\"MaxRetries\":$maxRetries,\"RetryTimer\":$retryTimer}}";
+}
+
+# Subscriptions:
+# $1 is sub-command: list, add, delete, modify
+
+sub do_subscriptions(@) {
+ my $subcommand = $_[0] || "";
+ shift;
+
+ my %subcommands = (
+ "list" => \&do_subscription_list,
+ "add" => \&do_subscription_add,
+ "delete" => \&do_subscription_delete,
+ "del" => \&do_subscription_delete,
+ "modify" => \&do_subscription_modify,
+ "mod" => \&do_subscription_modify
+ );
+ if (exists $subcommands{$subcommand}) {
+ $subcommands{$subcommand}(@_);
+ }
+ else {
+ print "$myname: unrecognized subscriptions subcommand $subcommand\n";
+ helphint();
+ $status=1
+ }
+}
+
+# list: With empty parameter, list all, else the parameter is
+# a subscriptionId
+
+sub do_subscription_list(@) {
+ my $urlpath=$base_subs;
+ my $subscriptionId = $_[0] || "";
+ if ($subscriptionId ne "") {
+ $urlpath = "$urlpath/$subscriptionId";
+ }
+ make_temps();
+ if (rest("GET", $urlpath)) {
+ if ($http_code eq "200") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID SUBSCRIPTION ID $subscriptionId";
+ }
+ elsif ($http_code eq "404") {
+ $error = "SUBSCRIPTION $subscriptionId NOT FOUND";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+}
+
+sub do_subscription_add(@) {
+ my $urlpath=$base_subs;
+
+ if (validate_subscription(@_)) {
+ make_temps();
+ if (rest("POST", $urlpath, make_subscriptionRequest(@_))) {
+ if ($http_code eq "201") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID INPUT";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+ }
+ else {
+ $status = 1;
+ }
+}
+
+sub do_subscription_delete(@) {
+ my $urlpath=$base_subs;
+ my $subscriptionId = $_[0] || "";
+ if ($subscriptionId ne "") {
+ $urlpath = "$urlpath/$subscriptionId";
+ }
+ else {
+ print "$myname: delete: Subscription id required\n";
+ $status=1;
+ return;
+ }
+ make_temps();
+ if (rest("DELETE", $urlpath)) {
+ if ($http_code eq "204") {
+ print "SUBSCRIPTION $subscriptionId DELETED\n";
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID SUBSCRIPTION ID $subscriptionId";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status = 1;
+ }
+ remove_temps();
+}
+
+sub do_subscription_modify(@) {
+ my $urlpath=$base_subs;
+ if (defined $_[0]) {
+ $urlpath = "$urlpath/$_[0]";
+ }
+ else {
+ print "$myname: modify: Subscription id required\n";
+ $status=1;
+ return;
+ }
+ shift;
+ if (validate_subscription(@_)) {
+ make_temps();
+ if (rest("PUT", $urlpath, make_subscriptionRequest(@_))) {
+ if ($http_code eq "200") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID INPUT";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+ }
+ else {
+ $status = 1;
+ }
+}
+
+sub do_health(@) {
+ my $urlpath=$base_health;
+ my $check = $_[0] || "";
+ # API now defines two types of checks, either of
+ # which must be specified.
+ if ($check ne "alive" and $check ne "ready") {
+ print "$myname: health check type required (alive or ready)\n";
+ $status=1;
+ return;
+ }
+ $urlpath = "$urlpath/$check";
+ make_temps();
+ if (rest("GET", $urlpath)) {
+ my $res;
+ if ($check eq "alive") {
+ # If GET succeeds at all, the xapp manager is alive, no
+ # need to check the HTTP code.
+ $res = "ALIVE";
+ }
+ else {
+ if ($http_code eq "200") {
+ $res = "READY";
+ }
+ elsif ($http_code eq "503") {
+ $res = "NOT READY";
+ }
+ elsif ($http_code eq "500") {
+ $res = "INTERNAL ERROR";
+ }
+ else {
+ $res = "UNKNOWN STATUS $http_code";
+ }
+ }
+ print "$res\n";
+ }
+ else {
+ $status = 1;
+ print "$myname: health check failed to contact appmgr\n";
+ }
+ remove_temps();
+}
+
+sub do_config(@) {
+ my $subcommand = $_[0] || "";
+ shift;
+
+ my %subcommands = (
+ "list" => \&do_config_list,
+ "add" => \&do_config_add,
+ "delete" => \&do_config_delete,
+ "del" => \&do_config_delete,
+ "modify" => \&do_config_modify,
+ "mod" => \&do_config_modify
+ );
+ if (exists $subcommands{$subcommand}) {
+ $subcommands{$subcommand}(@_);
+ }
+ else {
+ print "$myname: unrecognized config subcommand $subcommand\n";
+ helphint();
+ $status=1
+ }
+}
+
+sub do_config_list(@) {
+ if (defined $_[0]) {
+ print "$myname: \"config list\" has no parameters\n";
+ $status = 1;
+ return;
+ }
+ make_temps();
+ if (rest("GET", $base_config)) {
+ if ($http_code eq "200") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+}
+
+# validate_config() checks configuration commmand line.
+# "config add" and "config modify" expect either single parameter which
+# must be a JSON file that contains the whole thing to send (see API),
+# or 5 parameters, where the first three are
+# $_[0] = name
+# $_[1] = configName (name of the configMap)
+# $_[2] = namespace
+# Followed by two file names:
+# $_[3] = file containing configSchema
+# $_[4] = file containing data for configMap
+# Giving the last two literally on the command line does not make much sense,
+# since they are arbitrary JSON data.
+# On success, returns parameter count (1 or 5), depending on which kind of
+# command line found.
+# 0 if errors.
+
+# Check only the 3 names at the beginning of config add/modify/delete
+sub validate_config_names(@) {
+ my $retval = 1;
+ # Names in the Kubernetes world consist of lowercase alphanumerics
+ # and - and . as specified in
+ # https://kubernetes.io/docs/concepts/overview/working-with-objects/name
+ for (my $idx = 0; $idx <= 2; ++$idx) {
+ if (! ($_[$idx] =~ /^[a-z][-a-z0-9.]*$/)) {
+ print "$myname: invalid characters in name $_[$idx]\n";
+ $retval = 0;
+ }
+ }
+ return $retval;
+}
+
+sub validate_config(@) {
+ my $retval = 1;
+ print "validate_config args @_\n";
+ if ($#_ == 0) {
+ if (! -r $_[0]) {
+ print "$myname: config file $_[0] cannot be read: $!\n";
+ $retval = 0;
+ }
+ }
+ elsif ($#_ == 4) {
+ $retval = 5;
+ if (! validate_config_names(@_)) {
+ $retval = 0;
+ }
+ for (my $idx = 3; $idx <= 4; ++$idx) {
+ if (! -r $_[$idx]) {
+ print "$myname: cannot read file $_[$idx]\n";
+ $retval = 0;
+ }
+ }
+ }
+ else {
+ print "$myname: config add: 1 or 5 parameter expected\n";
+ $retval = 0;
+ }
+ return $retval;
+}
+
+# Generate JSON for the xAppConfig element (see API).
+
+sub make_xAppConfigInfo($$$) {
+ return "{\"xAppName\":\"$_[0]\",\"configMapName\":\"$_[1]\",\"namespace\":\"$_[2]\"}";
+}
+
+sub make_xAppConfig(@) {
+ my $retval = "{\"xAppConfigInfo\":" . make_xAppConfigInfo($_[0],$_[1],$_[2]);
+ my $fh;
+ open($fh, "<", $_[3]) or die "failed to open $_[3]";
+ my @obj = <$fh>;
+ close($fh);
+ $retval = $retval . ",\"configSchema\":" . join("", @obj);
+ open($fh, "<", $_[4]) or die "failed to open $_[4]";
+ @obj = <$fh>;
+ close($fh);
+ $retval = $retval . ",\"configMap\":" . join("", @obj) . "}";
+}
+
+sub do_config_add(@) {
+ my $paramCount;
+
+ $paramCount = validate_config(@_);
+ if ($paramCount > 0) {
+ my $xAppConfig;
+ if ($paramCount == 1) {
+ $xAppConfig = "\@$_[0]";
+ }
+ else {
+ $xAppConfig = make_xAppConfig(@_);
+ }
+ make_temps();
+ if (rest("POST", $base_config, $xAppConfig)) {
+ if ($http_code eq "201") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ elsif ($http_code eq "422") { # Validation failed, details in result
+ print_json $resultfile;
+ $status = 1;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID INPUT";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+ }
+ else {
+ $status = 1;
+ }
+}
+
+sub do_config_modify(@) {
+ my $paramCount;
+
+ $paramCount = validate_config(@_);
+ if ($paramCount > 0) {
+ my $xAppConfig;
+ if ($paramCount == 1) {
+ $xAppConfig = "\@$_[0]";
+ }
+ else {
+ $xAppConfig = make_xAppConfig(@_);
+ }
+ make_temps();
+ if (rest("PUT", $base_config, $xAppConfig)) {
+ if ($http_code eq "200") {
+ print_json $resultfile;
+ $status = 0;
+ }
+ elsif ($http_code eq "422") { # Validation failed, details in result
+ print_json $resultfile;
+ $status = 1;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID INPUT";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+ }
+ else {
+ $status = 1;
+ }
+}
+
+# In config delete, allow either 1 parameter naming a file that contains
+# a JSON xAppConfigInfo object, or 3 parameters giving the
+# components (xAppName, configMapName, namespace), same as
+# in add and modify operations.
+
+sub do_config_delete(@) {
+ my $xAppConfigInfo = "";
+
+ if ($#_ != 0 and $#_ != 2) {
+ print "$myname: wrong number of parameters for config delete\n";
+ $status = 1;
+ }
+ elsif ($#_ == 0) {
+ if (-r $_[0]) {
+ $xAppConfigInfo = "\@$_[0]";
+ }
+ else {
+ print "$myname: config file $_[0] cannot be read: $!\n";
+ $status = 1;
+ }
+ }
+ elsif (($#_ == 2) && validate_config_names(@_)) {
+ $xAppConfigInfo = make_xAppConfigInfo($_[0],$_[1],$_[2]);
+ }
+ else {
+ print "$myname: bad parameters for config delete\n";
+ $status = 1;
+ }
+ if ($xAppConfigInfo ne "") {
+ make_temps();
+ if (rest("DELETE", $base_config, $xAppConfigInfo)) {
+ if ($http_code eq "204") {
+ print "SUCCESFUL DELETION OF CONFIG\n";
+ $status = 0;
+ }
+ else {
+ my $error;
+ if ($http_code eq "400") {
+ $error = "INVALID PARAMETERS SUPPLIED";
+ }
+ elsif ($http_code eq "500") {
+ $error = "INTERNAL ERROR";
+ }
+ else {
+ $error = "UNKNOWN STATUS $http_code";
+ }
+ print "$error\n";
+ $status = 1;
+ }
+ }
+ else {
+ $status=1;
+ }
+ remove_temps();
+ }
+}