| #!/usr/bin/perl -w |
| # |
| # Copyright (c) 2019 AT&T Intellectual Property. |
| # Copyright (c) 2019 Nokia. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| ############################# |
| # Simple cli for xapp manager |
| # |
| # In addition to standard shell tools, requires basic Perl installation |
| # (Ubuntu package "perl-base", installed by default), packages "curl" and |
| # "yajl-tools" (the second provides json_reformat on Ubuntu; on Red Hat-style |
| # distributions install "yajl" instead). |
| # |
| use strict; |
| use Getopt::Long; |
| use Fcntl; |
| |
| my $myname="appmgrcli"; |
| |
| sub usage { |
| print <<"EOF1"; |
| usage: $myname [-h host] [-p port] [-v] [-c curlprog] command params... |
| - command is deploy, undeploy, status, subscriptions, health, config, help |
| - (abbreviations dep, undep, stat, subs, heal allowed) |
| - Parameters of the commands that may have parameters: |
| -- deploy: name of the xapp to deploy |
| ---- Deployment parameters come from the Help chart but the following can be |
| ---- overridden by option with the same name (for example --configName=cname): |
| ----- helmVersion ReleaseName namespace podHost (host of pods). |
| -- undeploy: name of the xapp to undeploy |
| -- status: |
| ---- No parameters: Lists information about all deployed xapps |
| ---- xapp name as parameter: Prints information about the given xapp |
| ---- xapp name and instance: Lists information about the given instance only |
| -- subscriptions is followed by sub-command list, add, delete, or modify |
| --- (abbreviations del and mod for delete and modify are allowed): |
| ---- list without parameters lists all subscriptions |
| ---- list with subscription id prints that subscription |
| ---- add URL eventType maxRetry retryTimer |
| ------- URL is the URL to notify |
| ------- eventType one of created,deleted,all |
| ------- maxRetry and retryTimer are positive decimal numbers |
| ---- modify id URL eventType maxRetry retryTimer |
| ------- id is the subscription id (find out with the list command) |
| --------the rest of the parameters are like in add |
| ---- delete id |
| ------- id is the subscription id to delete (find out with the list command) |
| -- config is followed by sub-command list, add, delete, or modify |
| --- (abbreviations del and mod for delete and modify are allowed): |
| ---- list (no pars) |
| ------ lists the configuration of all xapps |
| ---- add jsonfile |
| ------ Creates xapp configuration. the jsonfile must contain all data (see API) |
| ---- add name configName namespace configSchemaFile configDataFile |
| ------ Creates xapp configuration, but unlike in the 1-parameter form, |
| ------ Xapp name, config map name and namespace are separate parameters. |
| ------ The other data come from JSON files. |
| ---- modify |
| ------ Modifies existing configuration. Same parameters (1 or 5) as in add. |
| ---- delete name configName namespace |
| ------ Deletes the configuration identified by the parameters. |
| -------------------------------------------------------------- |
| - Default values for host and port can be set in environment |
| - variables APPMGR_HOST and APPMGR_PORT |
| - Option -v sets verbose mode. |
| - Option -c overrides the used curl program name ("curl" by default). |
| - Exit code is 0 for success, 1 for any kind of failure. |
| EOF1 |
| exit 0; |
| } |
| |
| sub helphint { |
| print "run $myname help (or --help) for instructions\n"; |
| } |
| |
| # Defaults |
| |
| my $host="localhost"; |
| my $port=8080; |
| my $verbose=0; |
| my $showhelp = 0; |
| |
| # API URLs |
| |
| my $base="/ric/v1"; |
| my $base_xapps="$base/xapps"; |
| my $base_health="$base/health"; |
| my $base_subs="$base/subscriptions"; |
| my $base_config="$base/config"; |
| |
| # Check for environment override |
| if (exists $ENV{"APPMGR_HOST"}) { |
| $host=$ENV{"APPMGR_HOST"}; |
| } |
| if (exists $ENV{"APPMGR_PORT"}) { |
| $port=$ENV{"APPMGR_PORT"}; |
| } |
| |
| # Overrides for some deploy parameters |
| |
| my $configName = ""; |
| my $namespace = "ricxapp"; |
| # Check for environment override |
| if (exists $ENV{"XAPP_NAMESPACE"}) { |
| $namespace=$ENV{"XAPP_NAMESPACE"}; |
| } |
| my $releaseName = ""; |
| my $helmVersion = "0.0.1"; |
| my $overrideFile = ""; |
| my $podHost = ""; |
| |
| # The curl command can be overridden for testing with a dummy. |
| |
| my $curl = "curl"; |
| |
| Getopt::Long::Configure("no_auto_abbrev", "permute"); |
| if (! GetOptions("h=s" => \$host, |
| "p=i" => \$port, |
| "c=s" => \$curl, |
| "ConfigName=s" => \$configName, |
| "Namespace=s" => \$namespace, |
| "ReleaseName=s" => \$releaseName, |
| "HelmVersion=s" => \$helmVersion, |
| "OverrideFile=s" => \$overrideFile, |
| "podHost=s" => \$podHost, |
| "help" => \$showhelp, |
| "v" => \$verbose)) { |
| print "$myname: Error in options\n"; |
| helphint(); |
| exit 1; |
| } |
| |
| if ($showhelp) { |
| usage(); |
| exit 0; |
| } |
| |
| if ($verbose) { |
| print "host = $host\n"; |
| print "port = $port\n"; |
| print "ConfigName = $configName\n"; |
| print "Namespace = $namespace\n"; |
| print "ReleaseName = $releaseName\n"; |
| print "HelmVersion = $helmVersion\n"; |
| print "OverrideFile = $overrideFile\n"; |
| print "podHost = $podHost\n"; |
| for (my $idx = 0; $idx <= $#ARGV; ++$idx) { |
| print "\$ARGV[$idx] = $ARGV[$idx]\n"; |
| } |
| } |
| |
| # Verify command and call handler function |
| |
| my %commands = ( |
| "deploy" => \&do_deploy, |
| "dep" => \&do_deploy, |
| "undeploy" => \&do_undeploy, |
| "undep" => \&do_undeploy, |
| "status" => \&do_status, |
| "stat" => \&do_status, |
| "subscriptions" => \&do_subscriptions, |
| "subs" => \&do_subscriptions, |
| "health" => \&do_health, |
| "heal" => \&do_health, |
| "config" => \&do_config, |
| "help" => \&usage |
| ); |
| |
| if ($#ARGV < 0) { |
| print "$myname: Missing command\n"; |
| helphint(); |
| exit 1; |
| } |
| |
| # Variable status used for the return value of the whole script. |
| my $status = 0; |
| |
| my $command = $ARGV[0]; |
| shift; |
| if (exists $commands{$command}) { |
| # Call the handler function with the rest of the command line |
| $commands{$command}(@ARGV); |
| exit $status; # Default exit. A handler can exit also if more convenient |
| } |
| print "$myname: Unrecognised command $command\n"; |
| helphint(); |
| exit 1; |
| |
| my $errfile; |
| my $resultfile; |
| |
| |
| sub make_temp_name($) { |
| my $tmpsuffix = "${$}${^T}"; |
| return "$_[0].$tmpsuffix"; |
| } |
| |
| sub make_temps { |
| $errfile = make_temp_name("/tmp/appmgr_e"); |
| $resultfile = make_temp_name("/tmp/appmgr_r"); |
| } |
| |
| sub remove_temps { |
| unlink ($errfile, $resultfile); |
| } |
| |
| sub print_file($$) { |
| my $outputhandle = $_[0]; |
| my $filename = $_[1]; |
| my $buffer; |
| my $inhandle; |
| if (!open($inhandle, "<", $filename)) { |
| print $outputhandle "$myname print_file: cannot open $filename: $!\n"; |
| return; |
| } |
| while (read($inhandle, $buffer, 4000) > 0) { |
| print $outputhandle $buffer; |
| } |
| close($inhandle); |
| } |
| |
| # The HTTP protocol result code, filled in by rest(). |
| |
| my $http_code = ""; |
| |
| # Helper: Given a curl output file, extract the number from ##code line. |
| # return ERROR if file cannot be opened, or "" if no code found. |
| |
| sub find_http_code($) { |
| my ($fh, $line, $code); |
| open($fh, "<", $_[0]) or return "ERROR"; |
| while ($line = <$fh>) { |
| if ($line =~ /^##([0-9]+)/) { |
| return $1; |
| } |
| } |
| return ""; |
| } |
| |
| # Helper for command execution: |
| # Do a rest call with "curl": $1 = method, $2 = path (without host and port |
| # which come from variables), $3 data to POST if needed |
| # returns true (1) if OK, and any returned data is in $resultfile |
| # else 0, and error message from curl is in $errfile, which is printed |
| # before returning the 0. |
| # |
| # On curl options: --silent --show-error disables progress bar, but allows |
| # error messages. --connect-timeout 20 limits waiting for connection to |
| # 20 seconds. In practice connection will succeed almost immediately, |
| # or in the case of wrong address not at all. |
| # To get the http code, using -w with format. The result comes at the end |
| # of the output, so "decorating" it for easier filtering. |
| # The code is put to global $http_code. |
| # |
| sub rest($$_) { |
| my $method = $_[0]; |
| my $path = $_[1]; |
| my $data = $_[2] || ""; |
| my $retval = 1; |
| my $http_status_file = make_temp_name("/tmp/appmgr_h"); |
| |
| # This redirects stderr (fd 2) to $errfile, but saving normal stderr |
| # so that if can be restored. |
| open(OLDERR, ">&", \*STDERR) or die "Can't dup STDERR: $!"; |
| open(ERRFILE, ">", $errfile) or die "open errorfile failed"; |
| open(STDERR, ">&", \*ERRFILE) or die "Can't dup ERRFILE: $!"; |
| |
| # This redirects stdout (fd 1) to $http_status_file, but saving original |
| # so that if can be restored. |
| open(OLDSTDOUT, ">&", \*STDOUT) or die "Can't dup STDOUT: $!"; |
| open(HTTP_STATUS_FILE, ">", $http_status_file) or die "open http status file failed"; |
| open(STDOUT, ">&", \*HTTP_STATUS_FILE) or die "Can't dup HTTP_STATUS_FILE: $!"; |
| |
| my @args = ($curl, "--silent", "--show-error", "--connect-timeout", "20", |
| "--header", "Content-Type: application/json", "-X", $method, |
| "-o", $resultfile, "-w", '\n##%{http_code}\n', |
| "http://${host}:${port}${path}"); |
| if ($data ne "") { |
| push(@args, "--data"); |
| push(@args, $data); |
| } |
| if ($verbose) { |
| print OLDSTDOUT "Running: " . join(" ", @args) . "\n"; |
| } |
| if (system(@args) == -1) { |
| print OLDSTDOUT "$myname: failed to execute @args\n"; |
| $retval = 0; |
| } |
| elsif ($? & 127) { |
| printf OLDSTDOUT "$myname: child died with signal %d, %s coredump\n", |
| ($? & 127), ($? & 128) ? 'with' : 'without'; |
| $retval = 0; |
| } |
| else { |
| my $curl_exit_code = $? >> 8; |
| if ($curl_exit_code == 0) { |
| seek HTTP_STATUS_FILE, 0, 0; # Ensures flushing |
| $http_code = find_http_code($http_status_file); |
| if ($http_code eq "ERROR") { |
| print OLDSTDOUT "$myname: failed to open temp file $http_status_file\n"; |
| $retval = 0; |
| } |
| elsif ($http_code eq "") { |
| print OLDSTDOUT "$myname: curl failed to provide HTTP code\n"; |
| $retval = 0; |
| } |
| else { |
| if ($verbose) { |
| print OLDSTDOUT "HTTP status code = $http_code\n"; |
| } |
| $retval = 1; # Interaction OK from REST point of view |
| } |
| } |
| else { |
| print_file(\*OLDSTDOUT, $errfile); |
| $retval = 0; |
| } |
| } |
| open(STDOUT, ">&", \*OLDSTDOUT) or die "Can't dup OLDSTDOUT: $!"; |
| open(STDERR, ">&", \*OLDERR) or die "Can't dup OLDERR: $!"; |
| unlink($http_status_file); |
| return $retval; |
| } |
| |
| # Pretty-print a JSON file to stdout. |
| # (currently uses json_reformat command) |
| # Skips the ##httpcode line we make "curl" |
| # add in order to get access to the HTTP status. |
| |
| sub print_json($) { |
| my $filename = $_[0]; |
| my ($line, $inhandle, $outhandle); |
| if (!open($inhandle, "<", $filename)) { |
| print "$myname print_json: cannot open $filename: $!\n"; |
| return; |
| } |
| if (!open($outhandle, "|json_reformat")) { |
| print "$myname print_json: cannot pipe to json_reformat: $!\n"; |
| return; |
| } |
| while ($line = <$inhandle>) { |
| if (! ($line =~ /^##[0-9]+/)) { |
| print $outhandle $line; |
| } |
| } |
| close($outhandle); |
| close($inhandle); |
| } |
| |
| # Append an entry like ","name":"value" to the first parameter, if "name" |
| # names a variable with non-empty value. |
| # Else returns the unmodified first parameter. |
| |
| sub append_option($$) { |
| my $result = $_[0]; |
| my $var = $_[1]; |
| my $val = eval("\$$var"); |
| if ($val ne "") { |
| $result = "$result,\"$var\":\"$val\""; |
| } |
| return $result; |
| } |
| |
| # Command handlers |
| # Assumes the API currently implemented. |
| # Functions for each command below |
| |
| # Deploy: |
| # The deploy command has one mandatory parameter "name" in the API, |
| # and several optional ones. Used mainly internally for testing, because |
| # they all override Helm chart values: |
| # "helmVersion": Helm chart version to be used |
| # "releaseName": The releas name of xApp visible in K8s |
| # "namespace": Name of the namespace to which xApp is deployed. |
| # "overrideFile": The file content used to override values.yaml file |
| # this host from the host the xapp manager is running in, we use the term |
| # and variable name "podHost" here. |
| # The options come from options (see GetOptions() call). |
| |
| sub do_deploy(@) { |
| my $name = $_[0] || ""; |
| if ($name ne "") { |
| my $data = "{\"XappName\":\"$name\""; |
| $data = append_option($data, "helmVersion"); |
| $data = append_option($data, "releaseName"); |
| $data = append_option($data, "namespace"); |
| $data = append_option($data, "overrideFile"); |
| $data = $data . "}"; |
| make_temps(); |
| if (rest("POST", $base_xapps, $data)) { |
| if ($http_code eq "201") { |
| print_json $resultfile; |
| $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(); |
| } |
| else { |
| print "$myname: Error: expected the name of xapp to deploy\n"; |
| $status = 1; |
| } |
| } |
| |
| sub do_undeploy(@) { |
| my $name = $_[0] || ""; |
| my $urlpath = $base_xapps; |
| if ($name ne "") { |
| make_temps(); |
| $urlpath = "$urlpath/$name"; |
| if (rest("DELETE", $urlpath)) { |
| if ($http_code eq "204") { |
| print "SUCCESSFUL DELETION\n"; |
| $status = 0; |
| } |
| else { |
| my $error; |
| if ($http_code eq "400") { |
| $error = "INVALID XAPP NAME 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(); |
| } |
| else { |
| print "$myname: Error: expected the name of xapp to undeploy\n"; |
| $status = 1; |
| } |
| } |
| |
| sub do_status(@) { |
| my $name = $_[0] || ""; |
| my $instance = $_[1] || ""; |
| my $urlpath = $base_xapps; |
| |
| if ($name ne "") { |
| $urlpath = "$urlpath/$name"; |
| } |
| if ($instance ne "") { |
| $urlpath = "$urlpath/instances/$instance" |
| } |
| 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 XAPP NAME SUPPLIED"; |
| } |
| if ($http_code eq "404") { |
| $error = "XAPP 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(); |
| } |
| |
| # Helpers for subscription: |
| # Validate the subscription data that follows a subscription add or modify |
| # subcommand. $1=URL, $2=eventType, $3=maxRetries, $4=retryTimer |
| # URL must look like URL, event type must be one of created deleted all, |
| # maxRetries and retryTimer must be non-negative numbers. |
| # If errors, returns false (0) and prints errors, else returns 1. |
| # |
| 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(); |
| } |
| } |