Browse Source

Viya4 v1 (#64)

* add properties file

* add get properties function

* add get of properties to listreports

* update listreports

* fix

* add insepct

* test with foldertree

* update properties

* test export folder tree

* exportfoldertree

* make cli location and command dynamic

* add imports

* fix

* add os

* add all option

* updates

* update import packages

* update import packages

* a few more sas-viya changes

* add function

* showsetup

* make targetname optional

* update

* update

* Update sharedfunctions.py

added  get_valid_filename

* Update snapshotreports.py

* Update updatepreferences.py

upped the limit on all users

* Update INSTALL.md

* Update INSTALL.md
master
Gerry Nelson 5 years ago
committed by GitHub
parent
commit
4de26a5398
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      INSTALL.md
  2. 2
      application.properties
  3. 31
      applyfolderauthorization.py
  4. 17
      createbinarybackup.py
  5. 42
      explainaccess.py
  6. 47
      exportfoldertree.py
  7. 12
      getpath.py
  8. 23
      importpackages.py
  9. 5
      listreports.py
  10. 29
      loginviauthinfo.py
  11. 405
      sharedfunctions.py
  12. 20
      showsetup.py
  13. 23
      snapshotreports.py
  14. 20
      testfolderaccess.py
  15. 75
      updatepreferences.py

18
INSTALL.md

@ -10,6 +10,24 @@ The tools should be installed on the same machine that hosts the Viya command-li
*git clone https://github.com/sassoftware/pyviyatools.git* *git clone https://github.com/sassoftware/pyviyatools.git*
NOTE: to use the tools with Viya 4 clone the viya4_v1 branch
*git clone https://github.com/sassoftware/pyviyatools.git -b viya4_v1*
**Configure**
The application.properties file contains the default location of the sas-admin or viya cli. Edit this file to match the location of the cli and the cli name in your environment.
The default values for Viya 3.x are:
sascli.location=/opt/sas/viya/home/bin/
sascli.executable=sas-admin
The default values for Viya 4.x are:
sascli.location=/opt/sas/viya/home/bin/
sascli.executable=sas-viya
**Authenticate** **Authenticate**
The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate. This process is documented in the SAS Viya Administration guide here. The pyviya tools use the sas-admin auth CLI to authenticate to Viya. To use the tool you must create a profile and authenticate. This process is documented in the SAS Viya Administration guide here.

2
application.properties

@ -0,0 +1,2 @@
sascli.location=/opt/sas/viya/home/bin/
sascli.executable=sas-viya

31
applyfolderauthorization.py

@ -10,14 +10,14 @@
# #
# Format of input csv file is 6 columns # Format of input csv file is 6 columns
# Column 1 is the full path to the folder # Column 1 is the full path to the folder
# Column 2 is the principal type # Column 2 is the principal type
# Column 3 is the principal name # Column 3 is the principal name
# Column 4 is the access setting (grant or prohibit) # Column 4 is the access setting (grant or prohibit)
# Column 5 is the permissions on the folder # Column 5 is the permissions on the folder
# Column 6 is the conveyed permissions on the folder's contents # Column 6 is the conveyed permissions on the folder's contents
# #
# For example: # For example:
# /gelcontent/gelcorp/marketing/reports,group,Marketing,grant,"read,add,remove","read,update,add,remove,delete,secure" # /gelcontent/gelcorp/marketing/reports,group,Marketing,grant,"read,add,remove","read,update,add,remove,delete,secure"
# #
# Copyright 2020, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. # Copyright 2020, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
# #
@ -38,14 +38,19 @@ import csv
import os import os
import json import json
import subprocess import subprocess
from sharedfunctions import callrestapi, getfolderid, file_accessible, printresult import sys
from sharedfunctions import callrestapi, getfolderid, file_accessible, printresult,getapplicationproperties
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get cli location from properties
clidir='/opt/sas/viya/home/bin/' propertylist=getapplicationproperties()
#clidir='c:\\admincli\\'
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
# setup command-line arguements clicommand=os.path.join(clidir,cliexe)
# setup command-line arguements
parser = argparse.ArgumentParser(description="Apply bulk auths from a CSV file to folders and contents") parser = argparse.ArgumentParser(description="Apply bulk auths from a CSV file to folders and contents")
parser.add_argument("-f","--file", help="Full path to CSV file. Format of csv: 'folderpath,principaltype,principalid,grant_or_prohibit,perms_on_folder,perms_on_contents",required='True') parser.add_argument("-f","--file", help="Full path to CSV file. Format of csv: 'folderpath,principaltype,principalid,grant_or_prohibit,perms_on_folder,perms_on_contents",required='True')
args = parser.parse_args() args = parser.parse_args()
@ -74,14 +79,14 @@ if check:
folderid=getfolderid(folderpath) folderid=getfolderid(folderpath)
folderuri=folderid[0] folderuri=folderid[0]
reqval='/folders/folders/'+folderuri reqval='/folders/folders/'+folderuri
# Construct JSON objects from auth rules defined in CSV. Two JSON objects are created for each row of CSV; one for perms on the folder object, one for conveyed perms on the object's contents. # Construct JSON objects from auth rules defined in CSV. Two JSON objects are created for each row of CSV; one for perms on the folder object, one for conveyed perms on the object's contents.
value_dict_object={"description":"Created by applyfolderauthorizations.py", value_dict_object={"description":"Created by applyfolderauthorizations.py",
"objectUri":reqval, "objectUri":reqval,
"permissions":folderpermissions.split(','), "permissions":folderpermissions.split(','),
"principalType":principaltype, "principalType":principaltype,
"principal":principalname, "principal":principalname,
"type":accesssetting "type":accesssetting
} }
value_dict_container={"description":"Created by applyfolderauthorizations.py", value_dict_container={"description":"Created by applyfolderauthorizations.py",
"containerUri":reqval, "containerUri":reqval,
@ -101,17 +106,17 @@ if check:
} }
constructed_bulk_rules_list.append(constructed_rule_dict_object) constructed_bulk_rules_list.append(constructed_rule_dict_object)
constructed_bulk_rules_list.append(constructed_rule_dict_container) constructed_bulk_rules_list.append(constructed_rule_dict_container)
else: else:
print("ERROR: cannot read "+file) print("ERROR: cannot read "+file)
print("Writing out bulk rule JSON file to bulk_rules_list.json") print("Writing out bulk rule JSON file to bulk_rules_list.json")
# Construct JSON schema containing rules # Construct JSON schema containing rules
bulk_rules_list_string=json.dumps(constructed_bulk_rules_list,indent=2) bulk_rules_list_string=json.dumps(constructed_bulk_rules_list,indent=2)
with open("bulk_rules_list.json", "w") as text_file: with open("bulk_rules_list.json", "w") as text_file:
text_file.write(bulk_rules_list_string+'\n') text_file.write(bulk_rules_list_string+'\n')
# Execute sas-admin CLI to apply rules from JSON schema # Execute sas-admin CLI to apply rules from JSON schema
command=clidir+'sas-admin authorization create-rules --file bulk_rules_list.json' command=clicommand+' authorization create-rules --file bulk_rules_list.json'
print("Executing command: "+command) print("Executing command: "+command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)

17
createbinarybackup.py

@ -23,8 +23,19 @@
# limitations under the License. # limitations under the License.
# #
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get python version
clidir='/opt/sas/viya/home/bin/' version=int(str(sys.version_info[0]))
# get cli location from properties
propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
debug=False debug=False
defaultBackupScheduleName="DEFAULT_BACKUP_SCHEDULE" defaultBackupScheduleName="DEFAULT_BACKUP_SCHEDULE"
newScheduleName="BINARY_BACKUP_SCHEDULE" newScheduleName="BINARY_BACKUP_SCHEDULE"
@ -104,7 +115,7 @@ if debug:
print('jobExecutionRequest_json:') print('jobExecutionRequest_json:')
print(jobExecutionRequest_json) print(jobExecutionRequest_json)
# STEP 3 of 4: Get the href to submit the job from the create jobExecution response # STEP 3 of 4: Get the href to submit the job from the create jobExecution response
links=jobExecutionRequest_json['links'] links=jobExecutionRequest_json['links']
href_found=False href_found=False

42
explainaccess.py

@ -21,7 +21,7 @@
# 4. As 1. with a header row and the folder path, which is useful if you concatenate sets of results in one file # 4. As 1. with a header row and the folder path, which is useful if you concatenate sets of results in one file
# ./explainaccess.py -f /folderA/folderB -p --header # ./explainaccess.py -f /folderA/folderB -p --header
# #
# 5. As 1. showing only rows which include a direct grant or prohibit # 5. As 1. showing only rows which include a direct grant or prohibit
# ./explainaccess.py -f /folderA/folderB --direct_only # ./explainaccess.py -f /folderA/folderB --direct_only
# #
# 6. Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions. # 6. Explain direct and indirect permissions on a service endpoint. Note in the results that there are no conveyed permissions.
@ -35,7 +35,7 @@
# since none of add, remove or create are applicable to a report. # since none of add, remove or create are applicable to a report.
# ./explainaccess.py -u /reports/reports/e2e0e601-b5a9-4601-829a-c5137f7441c6 --header -l read update delete secure # ./explainaccess.py -u /reports/reports/e2e0e601-b5a9-4601-829a-c5137f7441c6 --header -l read update delete secure
# #
# 9. Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness # 9. Explain direct and indirect permissions on a folder expressed as a URI. Keep the default permissions list, but for completeness
# we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs. # we must also specify -c true to request conveyed permissions be displayed, as they are not displayed by default for URIs.
# ./explainaccess.py -u /folders/folders/9145d26a-2c0d-4523-8835-ad186bb57fa6 --header -p -c true # ./explainaccess.py -u /folders/folders/9145d26a-2c0d-4523-8835-ad186bb57fa6 --header -p -c true
# #
@ -54,8 +54,25 @@
# limitations under the License. # limitations under the License.
# #
# Import Python modules
import argparse
import subprocess
import json
import sys
from sharedfunctions import getfolderid,callrestapi,getapplicationproperties
# get python version
version=int(str(sys.version_info[0]))
# get cli location from properties
propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
clidir='/opt/sas/viya/home/bin/'
debug=False debug=False
direct_only=False direct_only=False
valid_permissions=['read','update','delete','secure','add','remove','create'] valid_permissions=['read','update','delete','secure','add','remove','create']
@ -64,15 +81,6 @@ default_permissions=['read','update','delete','secure','add','remove']
direct_permission_suffix='*' direct_permission_suffix='*'
# Import Python modules
import argparse
import subprocess
import json
import sys
from sharedfunctions import getfolderid,callrestapi
# Define exception handler so that we only output trace info from errors when in debug mode # Define exception handler so that we only output trace info from errors when in debug mode
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook): def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
if debug: if debug:
@ -119,7 +127,7 @@ for permission in permissions:
if permission not in valid_permissions: if permission not in valid_permissions:
raise Exception(permission+' is not the name of a permission. Valid permissions are: '+' '.join(map(str, valid_permissions))) raise Exception(permission+' is not the name of a permission. Valid permissions are: '+' '.join(map(str, valid_permissions)))
# Two ways this program can be used: for a folder, or for a URI. # Two ways this program can be used: for a folder, or for a URI.
if path_to_folder: if path_to_folder:
getfolderid_result_json=getfolderid(path_to_folder) getfolderid_result_json=getfolderid(path_to_folder)
@ -141,7 +149,7 @@ if path_to_folder:
convey=False convey=False
else: else:
convey=True convey=True
else: else:
explainuri=objecturi explainuri=objecturi
# This tool explains the permissions of any object. # This tool explains the permissions of any object.
@ -161,7 +169,7 @@ else:
else: else:
convey=False convey=False
#Use the /authorization/decision endpoint to ask for an explanation of the rules that are relevant to principals on this URI #Use the /authorization/decision endpoint to ask for an explanation of the rules that are relevant to principals on this URI
#See Authorization API documentation in swagger at http://swagger.na.sas.com/apis/authorization/v4/apidoc.html#op:createExplanation #See Authorization API documentation in swagger at http://swagger.na.sas.com/apis/authorization/v4/apidoc.html#op:createExplanation
endpoint='/authorization/decision' endpoint='/authorization/decision'
@ -215,7 +223,7 @@ for pi in e:
# Permissions on object # Permissions on object
for permission in permissions: for permission in permissions:
# Not all objects have all the permissions # Not all objects have all the permissions
# Note that some objects do have permissions which are not meaningful for that object. # Note that some objects do have permissions which are not meaningful for that object.
# E.g. SASAdministrators are granted Add and Remove on reports, by an OOTB rule which grants SASAdministrators all permissions (including Add and Remove) on /**. # E.g. SASAdministrators are granted Add and Remove on reports, by an OOTB rule which grants SASAdministrators all permissions (including Add and Remove) on /**.
# Meanwhile, Add and Remove are not shown in the View or Edit Authotizations dialogs for reports in EV etc. # Meanwhile, Add and Remove are not shown in the View or Edit Authotizations dialogs for reports in EV etc.
# So, while it may be correct for the /authorization/decisions endpoint to explain that SASAdministrators are granted Add and Remove on a report, # So, while it may be correct for the /authorization/decisions endpoint to explain that SASAdministrators are granted Add and Remove on a report,
@ -271,7 +279,7 @@ for pi in e:
else: else:
# This permission was absent from the expanation for this principal # This permission was absent from the expanation for this principal
outstr=outstr+',' outstr=outstr+','
# Conveyed permissions # Conveyed permissions
if convey: if convey:
for permission in permissions: for permission in permissions:
# Not all objects have all the permissions # Not all objects have all the permissions

47
exportfoldertree.py

@ -7,7 +7,7 @@
# Pass in a directory and this tool will export the complete viya folder # Pass in a directory and this tool will export the complete viya folder
# structure to a sub-directory named for the current date and time # structure to a sub-directory named for the current date and time
# There will be a json file for each viya folder at the root level. # There will be a json file for each viya folder at the root level.
# #
# #
# Change History # Change History
# #
@ -30,15 +30,20 @@
# Import Python modules # Import Python modules
import argparse, sys, subprocess, uuid, time, os, glob import argparse, sys, subprocess, uuid, time, os, glob
from sharedfunctions import getfolderid, callrestapi from sharedfunctions import getfolderid, callrestapi,getapplicationproperties
# get python version # get python version
version=int(str(sys.version_info[0])) version=int(str(sys.version_info[0]))
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get cli location from properties
clidir='/opt/sas/viya/home/bin/' propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
# get input parameters # get input parameters
parser = argparse.ArgumentParser(description="Export the complete Viya folder tree") parser = argparse.ArgumentParser(description="Export the complete Viya folder tree")
parser.add_argument("-d","--directory", help="Directory for Export",required='True') parser.add_argument("-d","--directory", help="Directory for Export",required='True')
parser.add_argument("-q","--quiet", help="Suppress the are you sure prompt.", action='store_true') parser.add_argument("-q","--quiet", help="Suppress the are you sure prompt.", action='store_true')
@ -56,11 +61,11 @@ if os.path.exists(basedir):
if version > 2: if version > 2:
areyousure=input("The folder exists any existing json files in it will be deleted. Continue? (Y)") areyousure=input("The folder exists any existing json files in it will be deleted. Continue? (Y)")
else: else:
areyousure=raw_input("The folder already exists any existing json files in it will be deleted. Continue? (Y)") areyousure=raw_input("The folder already exists any existing json files in it will be deleted. Continue? (Y)")
else: else:
areyousure="Y" areyousure="Y"
else: areyousure="Y" else: areyousure="Y"
# prompt is Y if user selected Y, its a new directory, or user selected quiet mode # prompt is Y if user selected Y, its a new directory, or user selected quiet mode
if areyousure.upper() =='Y': if areyousure.upper() =='Y':
@ -69,7 +74,7 @@ if areyousure.upper() =='Y':
# create directory if it doesn't exist # create directory if it doesn't exist
if not os.path.exists(path): os.makedirs(path) if not os.path.exists(path): os.makedirs(path)
else: else:
filelist=glob.glob(path+"/*.json") filelist=glob.glob(path+"/*.json")
for file in filelist: for file in filelist:
os.remove(file) os.remove(file)
@ -81,31 +86,31 @@ if areyousure.upper() =='Y':
# loop root folders # loop root folders
if 'items' in resultdata: if 'items' in resultdata:
total_items=resultdata['count'] total_items=resultdata['count']
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
if total_items == 0: print("Note: No items returned.") if total_items == 0: print("Note: No items returned.")
else: else:
# export each folder and download the package file to the directory # export each folder and download the package file to the directory
for i in range(0,returned_items): for i in range(0,returned_items):
id=resultdata['items'][i]["id"] id=resultdata['items'][i]["id"]
package_name=str(uuid.uuid1()) package_name=str(uuid.uuid1())
json_name=resultdata['items'][i]["name"].replace(" ","")+'_'+str(i) json_name=resultdata['items'][i]["name"].replace(" ","")+'_'+str(i)
command=clidir+'sas-admin transfer export -u /folders/folders/'+id+' --name "'+package_name+'"' command=clicommand+' transfer export -u /folders/folders/'+id+' --name "'+package_name+'"'
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)
reqval='/transfer/packages?filter=eq(name,"'+package_name+'")' reqval='/transfer/packages?filter=eq(name,"'+package_name+'")'
package_info=callrestapi(reqval,reqtype) package_info=callrestapi(reqval,reqtype)
package_id=package_info['items'][0]['id'] package_id=package_info['items'][0]['id']
completefile=os.path.join(path,json_name+'.json') completefile=os.path.join(path,json_name+'.json')
command=clidir+'sas-admin transfer download --file '+completefile+' --id '+package_id command=clicommand+' transfer download --file '+completefile+' --id '+package_id
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)

12
getpath.py

@ -31,7 +31,17 @@
# #
clidir='/opt/sas/viya/home/bin/' # get python version
version=int(str(sys.version_info[0]))
# get cli location from properties
propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
debug=False debug=False
# Import Python modules # Import Python modules

23
importpackages.py

@ -15,18 +15,23 @@
# Licensed under the Apache License, Version 2.0 (the License); you may not use this file except in compliance with the License. # 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 # 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 # 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. # OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
# #
# change log # change log
# renamed to importpackages.py to be more descriptive of actual usage # renamed to importpackages.py to be more descriptive of actual usage
# #
# Import Python modules # Import Python modules
import argparse, sys, subprocess, os, json import argparse, sys, subprocess, os, json
from sharedfunctions import callrestapi from sharedfunctions import callrestapi, getapplicationproperties
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get cli location from properties
clidir='/opt/sas/viya/home/bin/' propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
# get input parameters # get input parameters
parser = argparse.ArgumentParser(description="Import JSON files from directory. All json files in directory will be imported.") parser = argparse.ArgumentParser(description="Import JSON files from directory. All json files in directory will be imported.")
@ -36,7 +41,7 @@ args= parser.parse_args()
basedir=args.directory basedir=args.directory
quietmode=args.quiet quietmode=args.quiet
# get python version # get python version
version=int(str(sys.version_info[0])) version=int(str(sys.version_info[0]))
@ -46,7 +51,7 @@ if not quietmode:
if version > 2: if version > 2:
areyousure=input("WARNING: If content from the packages already exists in folders it will be replaced. Continue? (Y)") areyousure=input("WARNING: If content from the packages already exists in folders it will be replaced. Continue? (Y)")
else: else:
areyousure=raw_input("WARNING:If content from the packages already exists in folders it will be replaced. Continue? (Y)") areyousure=raw_input("WARNING:If content from the packages already exists in folders it will be replaced. Continue? (Y)")
else: else:
areyousure="Y" areyousure="Y"
@ -62,7 +67,7 @@ if areyousure.upper() =='Y':
if filename.lower().endswith('.json'): if filename.lower().endswith('.json'):
#upload the json package #upload the json package
command=clidir+'sas-admin transfer upload --file '+os.path.join(basedir,filename)+'> /tmp/packageid.json' command=clicommand+' transfer upload --file '+os.path.join(basedir,filename)+'> /tmp/packageid.json'
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)
@ -74,7 +79,7 @@ if areyousure.upper() =='Y':
# get the packageid and import the package # get the packageid and import the package
packageid=package_data["id"] packageid=package_data["id"]
command=clidir+'sas-admin --output text -q transfer import --id '+packageid command=clicommand+' --output text -q transfer import --id '+packageid
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)

5
listreports.py

@ -27,14 +27,11 @@
# Import Python modules # Import Python modules
import argparse, sys, subprocess, uuid, time, os, glob import argparse, sys, subprocess, uuid, time, os, glob
from datetime import datetime as dt, timedelta as td from datetime import datetime as dt, timedelta as td
from sharedfunctions import getfolderid, callrestapi, getpath, printresult from sharedfunctions import getfolderid, callrestapi, getpath, printresult, getapplicationproperties
# get python version # get python version
version=int(str(sys.version_info[0])) version=int(str(sys.version_info[0]))
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION
clidir='/opt/sas/viya/home/bin/'
# get input parameters # get input parameters
parser = argparse.ArgumentParser(description="List Viya Reports and their folder path.") parser = argparse.ArgumentParser(description="List Viya Reports and their folder path.")
parser.add_argument("-n","--name", help="Name contains?",default=None) parser.add_argument("-n","--name", help="Name contains?",default=None)

29
loginviauthinfo.py

@ -47,7 +47,7 @@ import os
import argparse import argparse
import json import json
from sharedfunctions import file_accessible from sharedfunctions import file_accessible,getapplicationproperties
try: try:
# Python 3 # Python 3
@ -57,14 +57,19 @@ except ImportError:
from urlparse import urlparse from urlparse import urlparse
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get cli location from properties
clidir='/opt/sas/viya/home/bin/' propertylist=getapplicationproperties()
#clidir='c:\\admincli\\'
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
debug=0 debug=0
profileexists=0 profileexists=0
# get input parameters # get input parameters
parser = argparse.ArgumentParser(description="Authinfo File") parser = argparse.ArgumentParser(description="Authinfo File")
parser.add_argument("-f","--file", help="Enter the path to the authinfo file.",default='.authinfo') parser.add_argument("-f","--file", help="Enter the path to the authinfo file.",default='.authinfo')
args = parser.parse_args() args = parser.parse_args()
@ -84,12 +89,12 @@ badprofile=0
#profile does not exist #profile does not exist
if access_file==False: if access_file==False:
badprofile=1 badprofile=1
host='default' host='default'
#profile is empty file #profile is empty file
if os.stat(endpointfile).st_size==0: if os.stat(endpointfile).st_size==0:
badprofile=1 badprofile=1
host='default' host='default'
@ -124,11 +129,11 @@ if profileexists:
print('user: '+username) print('user: '+username)
print('profile: '+myprofile) print('profile: '+myprofile)
print('host: '+host) print('host: '+host)
#quote the password string for posix systems #quote the password string for posix systems
if (os.name =='posix'): command=clidir+"sas-admin --profile "+myprofile+ " auth login -u "+username+ " -p '"+password+"'" if (os.name =='posix'): command=clicommand+" --profile "+myprofile+ " auth login -u "+username+ " -p '"+password+"'"
else: command=clidir+'sas-admin --profile '+myprofile+ ' auth login -u '+username+ ' -p '+password else: command=clicommand+' --profile '+myprofile+ ' auth login -u '+username+ ' -p '+password
subprocess.call(command, shell=True) subprocess.call(command, shell=True)
else: else:
print('ERROR: '+fname+' does not exist') print('ERROR: '+fname+' does not exist')

405
sharedfunctions.py

@ -14,16 +14,16 @@
# #
# Change History # Change History
# #
# 27JAN2018 Comments added # 27JAN2018 Comments added
# 29JAN2018 Added simpleresults function # 29JAN2018 Added simpleresults function
# 31JAN2018 Added the ability to pass contenttype to call_rest_api (now callrestapi) # 31JAN2018 Added the ability to pass contenttype to call_rest_api (now callrestapi)
# 31JAN2018 Improved error handling of call_rest_api (now callrestapi) # 31JAN2018 Improved error handling of call_rest_api (now callrestapi)
# 31JAN2018 Deal with situation where json is not returned # 31JAN2018 Deal with situation where json is not returned
# 31JAN2018 Fix a bug when neither json or text is returned # 31JAN2018 Fix a bug when neither json or text is returned
# 02FEB2018 Fix a bug when text is returned # 02FEB2018 Fix a bug when text is returned
# 12MAR2018 Made simple result print generic # 12MAR2018 Made simple result print generic
# 20MAR2018 Added some comments # 20MAR2018 Added some comments
# 20MAR2018 Handle errors when profile and authentication token do not exist # 20MAR2018 Handle errors when profile and authentication token do not exist
# 20May2018 Fixed bug in authentication check # 20May2018 Fixed bug in authentication check
# 01jun2018 Deal with empty profile error # 01jun2018 Deal with empty profile error
# 23oct2018 Added print result function # 23oct2018 Added print result function
@ -58,29 +58,31 @@ import json
import pprint import pprint
import os import os
import collections import collections
import inspect
import re import re
pp = pprint.PrettyPrinter(indent=4) pp = pprint.PrettyPrinter(indent=4)
# validate rest api is not used at this time # validate rest api is not used at this time
# not used # not used
def validaterestapi(baseurl, reqval, reqtype, data={}): def validaterestapi(baseurl, reqval, reqtype, data={}):
global result global result
print("The request is a "+reqtype+" request: ",baseurl+reqval) print("The request is a "+reqtype+" request: ",baseurl+reqval)
json_data=json.dumps(data, ensure_ascii=False) json_data=json.dumps(data, ensure_ascii=False)
print("Data for Request:") print("Data for Request:")
print(json_data) print(json_data)
if (reqtype !="get" or reqtype !="post" or reqtype!="delete" or reqtype!="put"): if (reqtype !="get" or reqtype !="post" or reqtype!="delete" or reqtype!="put"):
print("NOTE: Invalid method") print("NOTE: Invalid method")
return; return;
# callrestapi # callrestapi
# this is the main function called many other programs and by the callrestapi program to make the REST calls # this is the main function called many other programs and by the callrestapi program to make the REST calls
# change history # change history
@ -88,26 +90,26 @@ def validaterestapi(baseurl, reqval, reqtype, data={}):
# 28oct2018 Added stop on error to be able to override stopping processing when an error occurs # 28oct2018 Added stop on error to be able to override stopping processing when an error occurs
def callrestapi(reqval, reqtype, acceptType='application/json', contentType='application/json',data={},stoponerror=1): def callrestapi(reqval, reqtype, acceptType='application/json', contentType='application/json',data={},stoponerror=1):
# get the url from the default profile # get the url from the default profile
baseurl=getbaseurl() baseurl=getbaseurl()
# get the auth token # get the auth token
oaval=getauthtoken(baseurl) oaval=getauthtoken(baseurl)
# build the authorization header # build the authorization header
head= {'Content-type':contentType,'Accept':acceptType} head= {'Content-type':contentType,'Accept':acceptType}
head.update({"Authorization" : oaval}) head.update({"Authorization" : oaval})
# maybe this can be removed # maybe this can be removed
global result global result
# sereliaze the data string for the request to json format # sereliaze the data string for the request to json format
json_data=json.dumps(data, ensure_ascii=False) json_data=json.dumps(data, ensure_ascii=False)
# call the rest api using the parameters passed in and the requests python library # call the rest api using the parameters passed in and the requests python library
if reqtype=="get": if reqtype=="get":
ret = requests.get(baseurl+reqval,headers=head,data=json_data) ret = requests.get(baseurl+reqval,headers=head,data=json_data)
elif reqtype=="post": elif reqtype=="post":
@ -119,20 +121,20 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app
else: else:
result=None result=None
print("NOTE: Invalid method") print("NOTE: Invalid method")
sys.exit() sys.exit()
# response error if status code between these numbers # response error if status code between these numbers
if (400 <= ret.status_code <=599): if (400 <= ret.status_code <=599):
print(ret.text) print(ret.text)
result=None result=None
if stoponerror: sys.exit() if stoponerror: sys.exit()
# return the result # return the result
else: else:
# is it json # is it json
try: try:
result=ret.json() result=ret.json()
except: except:
# is it text # is it text
@ -141,11 +143,11 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app
except: except:
result=None result=None
print("NOTE: No result to print") print("NOTE: No result to print")
return result; return result;
# getfolderid # getfolderid
# when a Viya content path is passed in return the id, path and uri # when a Viya content path is passed in return the id, path and uri
# change history # change history
@ -153,11 +155,11 @@ def callrestapi(reqval, reqtype, acceptType='application/json', contentType='app
# 08Feb2020 return full json as 4 item in list that is returned # 08Feb2020 return full json as 4 item in list that is returned
def getfolderid(path): def getfolderid(path):
# build the request parameters # build the request parameters
reqval="/folders/folders/@item?path="+path reqval="/folders/folders/@item?path="+path
reqtype='get' reqtype='get'
callrestapi(reqval,reqtype) callrestapi(reqval,reqtype)
if result==None: if result==None:
@ -169,10 +171,10 @@ def getfolderid(path):
targetid=result['id'] targetid=result['id']
targetname=result['name'] targetname=result['name']
targeturi="/folders/folders/"+targetid targeturi="/folders/folders/"+targetid
return [targetid,targeturi,targetname,result] return [targetid,targeturi,targetname,result]
# getbaseurl # getbaseurl
# from the default profile return the baseurl of the Viya server # from the default profile return the baseurl of the Viya server
# change history # change history
@ -180,7 +182,7 @@ def getfolderid(path):
# 01jun2018 Deal with empty profile error # 01jun2018 Deal with empty profile error
# 20nov2018 Use the SAS_CLI_PROFILE env variable # 20nov2018 Use the SAS_CLI_PROFILE env variable
def getbaseurl(): def getbaseurl():
# check that profile file is available and can be read # check that profile file is available and can be read
@ -202,13 +204,13 @@ def getbaseurl():
# get json from profile # get json from profile
with open(endpointfile) as json_file: with open(endpointfile) as json_file:
data = json.load(json_file) data = json.load(json_file)
# get the profile environment variable to use it # get the profile environment variable to use it
# if it is not set default to the default profile # if it is not set default to the default profile
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") cur_profile=os.environ.get("SAS_CLI_PROFILE","Default")
#print("URL: ",cur_profile ) #print("URL: ",cur_profile )
# check that information is in profile # check that information is in profile
if cur_profile in data: if cur_profile in data:
baseurl=data[cur_profile]['sas-endpoint'] baseurl=data[cur_profile]['sas-endpoint']
@ -223,57 +225,57 @@ def getbaseurl():
# getauthtoken # getauthtoken
# from the stored auth file get the authentication token for the request header # from the stored auth file get the authentication token for the request header
# change history # change history
# 01dec2017 initial development # 01dec2017 initial development
# return oaval=None when no authtoken retrieved # return oaval=None when no authtoken retrieved
# 20nov2018 Use the SAS_CLI_PROFILE env variable # 20nov2018 Use the SAS_CLI_PROFILE env variable
def getauthtoken(baseurl): def getauthtoken(baseurl):
#get authentication information for the header #get authentication information for the header
credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json')
# check that credential file is available and can be read # check that credential file is available and can be read
access_file=file_accessible(credential_file,'r') access_file=file_accessible(credential_file,'r')
if access_file==False: if access_file==False:
oaval=None oaval=None
print("ERROR: Cannot read authentication credentials at: ", credential_file) print("ERROR: Cannot read authentication credentials at: ", credential_file)
print("ERROR: Try refreshing your token with sas-admin auth login") print("ERROR: Try refreshing your token with sas-admin auth login")
sys.exit() sys.exit()
with open(credential_file) as json_file: with open(credential_file) as json_file:
data = json.load(json_file) data = json.load(json_file)
type(data) type(data)
# the sas-admin profile init creates an empty credential file # the sas-admin profile init creates an empty credential file
# check that credential is in file, if it is add it to the header, if not exit # check that credential is in file, if it is add it to the header, if not exit
# get the profile environment variable to use it # get the profile environment variable to use it
# if it is not set default to the default profile # if it is not set default to the default profile
cur_profile=os.environ.get("SAS_CLI_PROFILE","Default") cur_profile=os.environ.get("SAS_CLI_PROFILE","Default")
#print("LOGON: ", cur_profile ) #print("LOGON: ", cur_profile )
if cur_profile in data: if cur_profile in data:
oauthToken=data[cur_profile]['access-token'] oauthToken=data[cur_profile]['access-token']
oauthTokenType="bearer" oauthTokenType="bearer"
oaval=oauthTokenType + ' ' + oauthToken oaval=oauthTokenType + ' ' + oauthToken
head= {'Content-type':'application/json','Accept':'application/json' } head= {'Content-type':'application/json','Accept':'application/json' }
head.update({"Authorization" : oaval}) head.update({"Authorization" : oaval})
# test a connection to rest api if it fails exit # test a connection to rest api if it fails exit
r = requests.get(baseurl,headers=head) r = requests.get(baseurl,headers=head)
if (400 <= r.status_code <=599): if (400 <= r.status_code <=599):
oaval=None oaval=None
print(r.text) print(r.text)
print("ERROR: cannot connect to "+baseurl+" is your token expired?") print("ERROR: cannot connect to "+baseurl+" is your token expired?")
@ -285,69 +287,69 @@ def getauthtoken(baseurl):
print("ERROR: access token not in file: ", credential_file) print("ERROR: access token not in file: ", credential_file)
print("ERROR: Try refreshing your token with sas-admin auth login") print("ERROR: Try refreshing your token with sas-admin auth login")
sys.exit() sys.exit()
return oaval return oaval
# getinputjson # getinputjson
# load the returned json to a python dictionary # load the returned json to a python dictionary
# change history # change history
# 01dec2017 initial development # 01dec2017 initial development
def getinputjson(input_file): def getinputjson(input_file):
with open(input_file) as json_file: with open(input_file) as json_file:
inputdata = json.load(json_file) inputdata = json.load(json_file)
return inputdata return inputdata
# simpleresults # simpleresults
# take the complex json and create a simple print of the results # take the complex json and create a simple print of the results
# change history # change history
# 01dec2017 initial development # 01dec2017 initial development
# 20dec2018 simple output now alphibetical order by key # 20dec2018 simple output now alphibetical order by key
def simpleresults(resultdata): def simpleresults(resultdata):
# print a simplification of the json results # print a simplification of the json results
# list of items returned by rest call # list of items returned by rest call
if 'items' in resultdata: if 'items' in resultdata:
total_items=resultdata['count'] total_items=resultdata['count']
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
if total_items == 0: print("Note: No items returned.") if total_items == 0: print("Note: No items returned.")
for i in range(0,returned_items): for i in range(0,returned_items):
print ("=====Item ",i,"=======") print ("=====Item ",i,"=======")
origpairs=resultdata['items'][i] origpairs=resultdata['items'][i]
test=origpairs.get('description') test=origpairs.get('description')
if test==None: origpairs['description']='None' if test==None: origpairs['description']='None'
pairs=collections.OrderedDict(sorted(origpairs.items())) pairs=collections.OrderedDict(sorted(origpairs.items()))
for key,val in pairs.items(): for key,val in pairs.items():
if key != 'links': if key != 'links':
print(key,end="") print(key,end="")
print(" = ", val) print(" = ", val)
print("Result Summary: Total items available: ",total_items ,"Total items returned: ", returned_items) print("Result Summary: Total items available: ",total_items ,"Total items returned: ", returned_items)
elif 'id' in resultdata: #one item returned by rest call elif 'id' in resultdata: #one item returned by rest call
for key,val in resultdata.items(): for key,val in resultdata.items():
if key != 'links': if key != 'links':
print(key,end="") print(key,end="")
print(" = ", val) print(" = ", val)
else: else:
print("NOTE: No JSON Results Found") print("NOTE: No JSON Results Found")
@ -357,123 +359,123 @@ def simpleresults(resultdata):
# take the complex json and create a simple table of the results # take the complex json and create a simple table of the results
# change history # change history
# 01aug2018 initial development # 01aug2018 initial development
# 19dece2018 print csv in column orderwith only common columns # 19dece2018 print csv in column orderwith only common columns
def csvresults(resultdata,columns=[]): def csvresults(resultdata,columns=[]):
if 'items' in resultdata: if 'items' in resultdata:
total_items=resultdata['count'] total_items=resultdata['count']
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
if total_items == 0: print("Note: No items returned.") if total_items == 0: print("Note: No items returned.")
for i in range(0,returned_items): for i in range(0,returned_items):
origpairs=resultdata['items'][i] origpairs=resultdata['items'][i]
# create an ordered dictionary # create an ordered dictionary
pairs=collections.OrderedDict() pairs=collections.OrderedDict()
# loop thru the column list and insert to a new dictionary in that order # loop thru the column list and insert to a new dictionary in that order
# this ensures that colums appear in this order in the csv # this ensures that colums appear in this order in the csv
for keylabel in columns: for keylabel in columns:
# get the value for the current column # get the value for the current column
curval=origpairs.get(keylabel) curval=origpairs.get(keylabel)
if curval != None: if curval != None:
pairs[keylabel] = curval pairs[keylabel] = curval
else: else:
pairs[keylabel] = 'None' pairs[keylabel] = 'None'
numvals=len(columns) numvals=len(columns)
z=0 z=0
# print header row of column names # print header row of column names
for key,val in pairs.items(): for key,val in pairs.items():
z=z+1 z=z+1
# seperate with comma except last item # seperate with comma except last item
if z==numvals: sep='' if z==numvals: sep=''
else: sep=',' else: sep=','
if i==0 and key in columns: print(key,sep,end="") if i==0 and key in columns: print(key,sep,end="")
print("\n",end="") print("\n",end="")
z=0 z=0
# print rows # print rows
for key,val in pairs.items(): for key,val in pairs.items():
# seperate with comma except last item # seperate with comma except last item
z=z+1 z=z+1
if z==numvals: sep='' if z==numvals: sep=''
else: sep=',' else: sep=','
if key != 'links' and key in columns: print('"'+str(val)+'"'+sep, end="") if key != 'links' and key in columns: print('"'+str(val)+'"'+sep, end="")
print("\n",end="") print("\n",end="")
elif 'id' in resultdata: #one item returned by rest call elif 'id' in resultdata: #one item returned by rest call
numvals=len(resultdata.items()) numvals=len(resultdata.items())
z=0 z=0
for key,val in resultdata.items(): for key,val in resultdata.items():
# seperate with comma except last item # seperate with comma except last item
z=z+1 z=z+1
if z==numvals: sep='' if z==numvals: sep=''
else: sep=',' else: sep=','
if key != 'links': print(key,sep,end="") if key != 'links': print(key,sep,end="")
print("\n",end="") print("\n",end="")
z=0 z=0
for key,val in resultdata.items(): for key,val in resultdata.items():
# seperate with comma except last item # seperate with comma except last item
z=z+1 z=z+1
if z==numvals: sep='' if z==numvals: sep=''
else: sep=',' else: sep=','
if key != 'links': print('"'+str(val)+'"'+sep,end="") if key != 'links': print('"'+str(val)+'"'+sep,end="")
print("\n",end="") print("\n",end="")
else: else:
print("NOTE: No JSON Results Found") print("NOTE: No JSON Results Found")
# file_accessible # file_accessible
# Check if a file exists and is accessible. # Check if a file exists and is accessible.
# change history # change history
# 01dec2017 initial development # 01dec2017 initial development
def file_accessible(filepath, mode): def file_accessible(filepath, mode):
try: try:
f = open(filepath, mode) f = open(filepath, mode)
f.close() f.close()
except IOError as e: except IOError as e:
return False return False
return True return True
# printresult # printresult
# prints the results in the style requested # prints the results in the style requested
# change history # change history
# 28oct2018 initial development # 28oct2018 initial development
# 22dec2018 add csv columns only relevent for csv output, defaults provided but can be overriden when called # 22dec2018 add csv columns only relevent for csv output, defaults provided but can be overriden when called
@ -481,10 +483,10 @@ def file_accessible(filepath, mode):
def printresult(result,output_style,colsforcsv=["id","name","type","description","creationTimeStamp","modifiedTimeStamp"]): def printresult(result,output_style,colsforcsv=["id","name","type","description","creationTimeStamp","modifiedTimeStamp"]):
# print rest call results # print rest call results
if type(result) is dict: if type(result) is dict:
if output_style=='simple': if output_style=='simple':
simpleresults(result) simpleresults(result)
elif output_style=='simplejson': elif output_style=='simplejson':
@ -493,54 +495,54 @@ def printresult(result,output_style,colsforcsv=["id","name","type","description"
csvresults(result,columns=colsforcsv) csvresults(result,columns=colsforcsv)
else: else:
print(json.dumps(result,indent=2)) print(json.dumps(result,indent=2))
else: print(result) else: print(result)
# getprofileinfo # getprofileinfo
# prints the token expiration, endpoint and current user # prints the token expiration, endpoint and current user
# change history # change history
# 20nov2018 initial development # 20nov2018 initial development
def getprofileinfo(myprofile): def getprofileinfo(myprofile):
#get authentication information for the header #get authentication information for the header
credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json') credential_file=os.path.join(os.path.expanduser('~'),'.sas','credentials.json')
# check that credential file is available and can be read # check that credential file is available and can be read
access_file=file_accessible(credential_file,'r') access_file=file_accessible(credential_file,'r')
if access_file==False: if access_file==False:
print("ERROR: Cannot read authentication credentials at: ", credential_file) print("ERROR: Cannot read authentication credentials at: ", credential_file)
print("ERROR: Try refreshing your token with sas-admin auth login") print("ERROR: Try refreshing your token with sas-admin auth login")
sys.exit() sys.exit()
with open(credential_file) as json_file: with open(credential_file) as json_file:
data = json.load(json_file) data = json.load(json_file)
type(data) type(data)
# the sas-admin profile init creates an empty credential file # the sas-admin profile init creates an empty credential file
# check that credential is in file, if it is add it to the header, if not exit # check that credential is in file, if it is add it to the header, if not exit
# get the profile environment variable to use it # get the profile environment variable to use it
# if it is not set default to the default profile # if it is not set default to the default profile
if myprofile in data: if myprofile in data:
expiry=data[myprofile]['expiry'] expiry=data[myprofile]['expiry']
print("Note your authentication token expires at: "+expiry) print("Note your authentication token expires at: "+expiry)
else: else:
print("ERROR: access token not in file: ", credential_file) print("ERROR: access token not in file: ", credential_file)
print("ERROR: Try refreshing your token with sas-admin auth login") print("ERROR: Try refreshing your token with sas-admin auth login")
sys.exit() sys.exit()
# note the path to the profile is hard-coded right now # note the path to the profile is hard-coded right now
endpointfile=os.path.join(os.path.expanduser('~'),'.sas','config.json') endpointfile=os.path.join(os.path.expanduser('~'),'.sas','config.json')
access_file=file_accessible(endpointfile,'r') access_file=file_accessible(endpointfile,'r')
@ -558,30 +560,30 @@ def getprofileinfo(myprofile):
# get json from profile # get json from profile
with open(endpointfile) as json_file: with open(endpointfile) as json_file:
data = json.load(json_file) data = json.load(json_file)
# check that information is in profile # check that information is in profile
if myprofile in data: if myprofile in data:
baseurl=data[myprofile]['sas-endpoint'] baseurl=data[myprofile]['sas-endpoint']
print("Endpoint is: "+baseurl) print("Endpoint is: "+baseurl)
else: else:
print("ERROR: profile "+myprofile+" does not exist. Recreate profile with sas-admin profile init.") print("ERROR: profile "+myprofile+" does not exist. Recreate profile with sas-admin profile init.")
# build the request parameters # build the request parameters
reqval="/identities/users/@currentUser" reqval="/identities/users/@currentUser"
reqtype='get' reqtype='get'
result=callrestapi(reqval,reqtype) result=callrestapi(reqval,reqtype)
if result==None: if result==None:
print("NOTE: Not logged in.") print("NOTE: Not logged in.")
else: else:
print("Logged on as id: "+ result['id']) print("Logged on as id: "+ result['id'])
print("Logged on as name: "+result['name']) print("Logged on as name: "+result['name'])
# getpath # getpath
# when a Viya objectURI is passed in return the path # when a Viya objectURI is passed in return the path
# change history # change history
@ -613,7 +615,7 @@ def getpath(objecturi):
return path return path
# getidsanduris # getidsanduris
# given a result json structure, return a dictionary with a list of id's and uri's # given a result json structure, return a dictionary with a list of id's and uri's
# change history # change history
# 01dec2017 initial development # 01dec2017 initial development
@ -622,52 +624,52 @@ def getidsanduris(resultdata):
resultdict={} resultdict={}
resultdict['ids']=[] resultdict['ids']=[]
resultdict['uris']=[] resultdict['uris']=[]
# loop the result and add a list of ids and uris to the returned dictionary # loop the result and add a list of ids and uris to the returned dictionary
if 'items' in resultdata: if 'items' in resultdata:
total_items=resultdata['count'] total_items=resultdata['count']
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
if total_items == 0: print("Note: No items returned.") if total_items == 0: print("Note: No items returned.")
for i in range(0,returned_items): for i in range(0,returned_items):
resultdict['ids'].append(resultdata['items'][i]['id']) resultdict['ids'].append(resultdata['items'][i]['id'])
resultdict['uris'].append(resultdata['items'][i]['uri']) resultdict['uris'].append(resultdata['items'][i]['uri'])
return resultdict return resultdict
# simplejsonresults # simplejsonresults
# given a result json structure, remove all the "links" items # given a result json structure, remove all the "links" items
# this will return a more readable json output # this will return a more readable json output
# change history # change history
# 20feb2020 initial development # 20feb2020 initial development
def simplejsonresults(resultdata): def simplejsonresults(resultdata):
if 'items' in resultdata: # list of items returned by rest call if 'items' in resultdata: # list of items returned by rest call
for key in list(resultdata): for key in list(resultdata):
if key == 'links': del resultdata[key] if key == 'links': del resultdata[key]
total_items=resultdata['count'] total_items=resultdata['count']
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
if total_items == 0: print("Note: No items returned.") if total_items == 0: print("Note: No items returned.")
for i in range(0,returned_items): for i in range(0,returned_items):
for key in list(resultdata['items'][i]): for key in list(resultdata['items'][i]):
if key=='links': if key=='links':
del resultdata['items'][i][key] del resultdata['items'][i][key]
print(json.dumps(resultdata,indent=2)) print(json.dumps(resultdata,indent=2))
elif 'id' in resultdata: #one item returned by rest call elif 'id' in resultdata: #one item returned by rest call
del resultdata['links'] del resultdata['links']
print(json.dumps(resultdata,indent=2)) print(json.dumps(resultdata,indent=2))
@ -684,3 +686,18 @@ def simplejsonresults(resultdata):
def get_valid_filename(s): def get_valid_filename(s):
s = str(s).strip().replace(' ', '_') s = str(s).strip().replace(' ', '_')
return re.sub(r'(?u)[^-\w.]', '', s) return re.sub(r'(?u)[^-\w.]', '', s)
# getapplicationproperties
# 20nov2020 initial development
def getapplicationproperties():
# get the path for the script file this is where the properties file will bbe
thepath=os.path.split(inspect.getsourcefile(lambda:0))
install_dir=thepath[0]
prop_file=os.path.join(install_dir, "application.properties")
myparams=dict(line.strip().split('=') for line in open(prop_file) if line[0].isalpha())
return myparams

20
showsetup.py

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# showsetup.py # showsetup.py
# #
# output some system settings to help with debugging issues # output some system settings to help with debugging issues
# #
# October 2018 # October 2018
@ -30,7 +30,7 @@ import sys
import requests import requests
import os import os
from sharedfunctions import getprofileinfo from sharedfunctions import getprofileinfo, getapplicationproperties
# software versions # software versions
print("Python Version is: "+str(sys.version_info[0])+'.'+str(sys.version_info[1])) print("Python Version is: "+str(sys.version_info[0])+'.'+str(sys.version_info[1]))
@ -40,7 +40,7 @@ print("Requests Version is: "+requests.__version__)
cur_profile=os.environ.get("SAS_CLI_PROFILE","NOTSET") cur_profile=os.environ.get("SAS_CLI_PROFILE","NOTSET")
if cur_profile=="NOTSET": if cur_profile=="NOTSET":
print("SAS_CLI_PROFILE environment variable not set, using Default profile") print("SAS_CLI_PROFILE environment variable not set, using Default profile")
cur_profile='Default' cur_profile='Default'
else: else:
@ -49,7 +49,7 @@ else:
ssl_file=os.environ.get("SSL_CERT_FILE","NOTSET") ssl_file=os.environ.get("SSL_CERT_FILE","NOTSET")
if ssl_file=="NOTSET": if ssl_file=="NOTSET":
print("SSL_CERT_FILE environment variable not set.") print("SSL_CERT_FILE environment variable not set.")
else: else:
print("SSL_CERT_FILE environment variable set to profile "+ ssl_file) print("SSL_CERT_FILE environment variable set to profile "+ ssl_file)
@ -57,9 +57,19 @@ else:
r_ssl_file=os.environ.get("REQUESTS_CA_BUNDLE","NOTSET") r_ssl_file=os.environ.get("REQUESTS_CA_BUNDLE","NOTSET")
if r_ssl_file=="NOTSET": if r_ssl_file=="NOTSET":
print("REQUESTS_CA_BUNDLE environment variable not set.") print("REQUESTS_CA_BUNDLE environment variable not set.")
else: else:
print("REQUESTS_CA_BUNDLE environment variable set to profile "+ r_ssl_file) print("REQUESTS_CA_BUNDLE environment variable set to profile "+ r_ssl_file)
getprofileinfo(cur_profile) getprofileinfo(cur_profile)
# get cli location from properties
propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
print(propertylist)

23
snapshotreports.py

@ -42,13 +42,20 @@
import re import re
import argparse, sys, subprocess, uuid, time, os, glob import argparse, sys, subprocess, uuid, time, os, glob
from datetime import datetime as dt, timedelta as td from datetime import datetime as dt, timedelta as td
from sharedfunctions import getfolderid, callrestapi, getpath, get_valid_filename from sharedfunctions import getfolderid, callrestapi, getpath, getapplicationproperties, get_valid_filename
# get python version # get python version
version=int(str(sys.version_info[0])) version=int(str(sys.version_info[0]))
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION # get cli location from properties
clidir='/opt/sas/viya/home/bin/' propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
# get input parameters # get input parameters
parser = argparse.ArgumentParser(description="Export Viya Reports each to its own unique transfer package") parser = argparse.ArgumentParser(description="Export Viya Reports each to its own unique transfer package")
@ -150,7 +157,7 @@ if areyousure.upper() =='Y':
json_name=get_valid_filename(path_to_report+resultdata['items'][i]["name"].replace(" ","")+'_'+str(i)) json_name=get_valid_filename(path_to_report+resultdata['items'][i]["name"].replace(" ","")+'_'+str(i))
command=clidir+'sas-admin transfer export -u /reports/reports/'+id+' --name "'+package_name+'"' command=clicommand+' transfer export -u /reports/reports/'+id+' --name "'+package_name+'"'
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)
@ -160,20 +167,18 @@ if areyousure.upper() =='Y':
package_id=package_info['items'][0]['id'] package_id=package_info['items'][0]['id']
completefile=os.path.join(path,json_name+'.json') completefile=os.path.join(path,json_name+'.json')
command=clidir+'sas-admin transfer download --file '+completefile+' --id '+package_id command=clicommand+' transfer download --file '+completefile+' --id '+package_id
print(command) print(command)
subprocess.call(command, shell=True) subprocess.call(command, shell=True)
#time.sleep(1) #time.sleep(1)
if autotranferremove: if autotranferremove:
print(clidir+'sas-admin transfer delete --id '+package_id+"\n") print(clicommand+' transfer delete --id '+package_id+"\n")
remTransferObject = subprocess.Popen(clidir+'sas-admin transfer delete --id '+package_id, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) remTransferObject = subprocess.Popen(clicommand+' transfer delete --id '+package_id, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
remTransferObjectOutput = remTransferObject.communicate(b'Y\n') remTransferObjectOutput = remTransferObject.communicate(b'Y\n')
remTransferObject.wait() remTransferObject.wait()
print("NOTE: "+str(reports_exported)+" report(s) exported to json files in "+path)
print("NOTE: "+str(total_items)+" total reports found, "+str(reports_exported)+" reports exported to json files in "+path) print("NOTE: "+str(total_items)+" total reports found, "+str(reports_exported)+" reports exported to json files in "+path)
else: else:
print("NOTE: Operation cancelled") print("NOTE: Operation cancelled")

20
testfolderaccess.py

@ -24,9 +24,6 @@
# limitations under the License. # limitations under the License.
# #
# CHANGE THIS VARIABLE IF YOUR CLI IS IN A DIFFERENT LOCATION
clidir='/opt/sas/viya/home/bin/'
debug=False
# Import Python modules # Import Python modules
@ -34,8 +31,23 @@ import argparse
import subprocess import subprocess
import json import json
import sys import sys
import os
from sharedfunctions import getfolderid,callrestapi,getapplicationproperties
# get python version
version=int(str(sys.version_info[0]))
from sharedfunctions import getfolderid,callrestapi # get cli location from properties
propertylist=getapplicationproperties()
clidir=propertylist["sascli.location"]
cliexe=propertylist["sascli.executable"]
clicommand=os.path.join(clidir,cliexe)
debug=False
# Define exception handler so that we only output trace info from errors when in debug mode # Define exception handler so that we only output trace info from errors when in debug mode
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook): def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):

75
updatepreferences.py

@ -8,7 +8,7 @@
# #
# Change History # Change History
# #
# 30OCT2018 first version # 30OCT2018 first version
# #
# Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. # Copyright © 2018, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
# #
@ -36,18 +36,18 @@
#################################################################### ####################################################################
#### POSSIBLE VALUES #### #### POSSIBLE VALUES ####
#################################################################### ####################################################################
#### sas.welcome.suppress = true/false #### #### sas.welcome.suppress = true/false ####
#### sas.drive.show.pinned = true/false #### #### sas.drive.show.pinned = true/false ####
#### VA.geo.drivedistance.unit = kilometers/miles #### #### VA.geo.drivedistance.unit = kilometers/miles ####
#### OpenUI.Theme.Default = sas_corporate/sas_inspire/sas_hcb #### #### OpenUI.Theme.Default = sas_corporate/sas_inspire/sas_hcb ####
#################################################################### ####################################################################
import argparse import argparse
from sharedfunctions import callrestapi from sharedfunctions import callrestapi
parser = argparse.ArgumentParser(description="Update user preferences for a user or a group of users") parser = argparse.ArgumentParser(description="Update user preferences for a user or a group of users")
parser.add_argument("-t", "--target", help="Type the target of the update: user or group", required=True, choices=['user', 'group']) parser.add_argument("-t", "--target", help="Type the target of the update: user or group", required=True, choices=['user', 'group','all'])
parser.add_argument("-tn", "--targetname", help="ID of the user or group to which the update applies.", required=True) parser.add_argument("-tn", "--targetname", help="ID of the user or group to which the update applies.")
parser.add_argument("-pi", "--preferenceid", help="ID of the preference to be updated", required=True) parser.add_argument("-pi", "--preferenceid", help="ID of the preference to be updated", required=True)
parser.add_argument("-pv", "--preferencevalue", help="Value to be set for the preference", required=True) parser.add_argument("-pv", "--preferencevalue", help="Value to be set for the preference", required=True)
@ -59,41 +59,62 @@ preferenceValue = args.preferencevalue
json= {"application": "SAS Visual Analytics", "version": 1,"id": preferenceID ,"value": preferenceValue} json= {"application": "SAS Visual Analytics", "version": 1,"id": preferenceID ,"value": preferenceValue}
# Function to update preference of a specific user
if target == 'user' : # apply for all users in a deployment
if target=='all' :
reqtype='get'
reqval='/identities/users/?limit=10000'
resultdata=callrestapi(reqval,reqtype)
reqtype="put"
if 'items' in resultdata:
returned_items=len(resultdata['items'])
for i in range(0,returned_items):
id=resultdata['items'][i]['id']
type=resultdata['items'][i]['type']
if type=="user":
reqval="/preferences/preferences/"+ id +"/" + preferenceID
result=callrestapi(reqval, reqtype,data=json,stoponerror=0)
print("Updating Preference "+reqval+" = "+preferenceValue)
elif target == 'user' :
userID=targetName userID=targetName
reqtype='get' reqtype='get'
reqval="/identities/users/"+userID reqval="/identities/users/"+userID
userexist=callrestapi(reqval,reqtype) userexist=callrestapi(reqval,reqtype)
reqtype="put" reqtype="put"
reqval="/preferences/preferences/"+ userID +"/" + preferenceID reqval="/preferences/preferences/"+ userID +"/" + preferenceID
result=callrestapi(reqval,reqtype,data=json) result=callrestapi(reqval,reqtype,data=json)
print("Updating Preference "+reqval+" = "+preferenceValue) print("Updating Preference "+reqval+" = "+preferenceValue)
else: # Execute actual code to update the preference for a user or a group else: # Execute actual code to update the preference for a user or a group
reqtype='get' reqtype='get'
reqval='/identities/groups/'+ targetName +'/members?limit=1000' reqval='/identities/groups/'+ targetName +'/members?limit=1000&depth=-1'
resultdata=callrestapi(reqval,reqtype) resultdata=callrestapi(reqval,reqtype,contentType="application/vnd.sas.identity.group.member.flat")
reqtype="put" reqtype="put"
if 'items' in resultdata: if 'items' in resultdata:
returned_items=len(resultdata['items']) returned_items=len(resultdata['items'])
for i in range(0,returned_items): for i in range(0,returned_items):
id=resultdata['items'][i]['id'] id=resultdata['items'][i]['id']
type=resultdata['items'][i]['type'] type=resultdata['items'][i]['type']
if type=="user": if type=="user":
reqval="/preferences/preferences/"+ id +"/" + preferenceID reqval="/preferences/preferences/"+ id +"/" + preferenceID
result=callrestapi(reqval, reqtype,data=json,stoponerror=0) result=callrestapi(reqval, reqtype,data=json,stoponerror=0)
print(result)
print("Updating Preference "+reqval+" = "+preferenceValue) print("Updating Preference "+reqval+" = "+preferenceValue)
else: print("Cannot set preferences for a group "+id ) else: print("Cannot set preferences for a group "+id )

Loading…
Cancel
Save