diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f636bac --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Aktuelle Datei", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/exportConfig.py b/exportConfig.py index 0350de4..6bc0950 100644 --- a/exportConfig.py +++ b/exportConfig.py @@ -44,7 +44,8 @@ def set_env(env, time, path): # [AA 2021.12.10] Method to call process synchronously def run_proc(proc, data): try: - process = subprocess.Popen([proc, "export"] + data) + inputParam = [proc, "export"] + data + process = subprocess.Popen(inputParam) outs = process.wait(timeout=10*60) print("[DEBUG]", "Process return code:", outs) except subprocess.TimeoutExpired: @@ -59,29 +60,27 @@ def copy(src, dst): # [AA, EW 2021.12.13] Check if the path exists and decide between folder or file -def checkdir(d): - dflag = False +def checkdir(path): + isFolder = False + isDeleted = False - # [AA 2021.12.13] If the path exists and the associated path points to a directory - if os.path.exists(d) and os.path.isdir(d): + # [AA 2021.12.13] If the path exists and the associated path is a folder + if os.path.exists(path) and os.path.isdir(path): + isFolder = True - # [AA 2021.12.13] If the directory exists, but is empty, delete directory - if not os.listdir(d): + # [AA 2021.12.13] If the folder exists, but is empty, delete folder + if not os.listdir(path): + isDeleted = True # [AA 2021.12.13] Delete Folder if empty - print("[DEBUG]", "Deleting empty directory:", d) - os.rmdir(d) - else: - print("[DEBUG]", "Found directory:", d) - dflag = True - else: - print("[DEBUG]", "Found file:", d) - return d, dflag + os.rmdir(path) + + return isFolder, isDeleted # [AA 2021.12.13] Fill dictionary -def readfile(d, f): - with open(os.path.join(d, f), 'r') as cfg: +def readfile(path): + with open(path, 'r', encoding='utf8') as cfg: # [AA 2021.12.01] Load the content of the particular resource file in eg: management_zones obj = hcl.load(cfg) @@ -92,7 +91,7 @@ def readfile(d, f): return key, val -# [AA 2021.12.13] Add kay, value to dictionary +# [AA 2021.12.13] Add key, value to dictionary def addentry(key, val, myDict): myDict.setdefault(key, []) if(val) not in myDict.get(key): @@ -108,56 +107,56 @@ def writefile(k, d): # [AA 2021.12.10] Set timestamp once when script is executed timestamp = time.strftime("%Y%m%d-%H%M%S") +#timestamp = time.strftime("") # [AA 2021.11.29] Load enviroment file load_dotenv() # [AA 2021.12.10] Set environments/tenants -Environments = ["EMEA_PROD" -# "EMEA_PREPROD", -# "NA_PROD", -# "NA_PREPROD", -# "CN_PROD", -# "CN_PREPROD" +Environments = [ + "EMEA_PROD" + # "EMEA_PREPROD", + # "NA_PROD", + # "NA_PREPROD", + # "CN_PROD", + # "CN_PREPROD" ] # [AA 2021.12.10] Set available resources -Resources = [ # "dynatrace_custom_service" +Resources = [ + "dynatrace_custom_service", "dynatrace_dashboard", "dynatrace_management_zone", - # "dynatrace_maintenance_window", - # "dynatrace_request_attribute", + "dynatrace_maintenance_window", + "dynatrace_request_attribute", "dynatrace_alerting_profile", "dynatrace_notification", - # "dynatrace_autotag" - # "dynatrace_aws_credentials", - # "dynatrace_azure_credentials", - # "dynatrace_k8s_credentials", - # "dynatrace_service_anomalies", - # "dynatrace_application_anomalies", - # "dynatrace_host_anomalies", - # "dynatrace_database_anomalies", - # "dynatrace_custom_anomalies", - # "dynatrace_disk_anomalies", - # "dynatrace_calculated_service_metric", - # "dynatrace_service_naming", - # "dynatrace_host_naming", - # "dynatrace_processgroup_naming", - # "dynatrace_slo", - # "dynatrace_span_entry_point", - # "dynatrace_span_capture_rule", - # "dynatrace_span_context_propagation", - # "dynatrace_resource_attributes", - # "dynatrace_span_attribute", - # "dynatrace_mobile_application", - # "dynatrace_credentials", - # "dynatrace_browser_monitor", - # "dynatrace_http_monitor", + "dynatrace_autotag" + "dynatrace_aws_credentials", + "dynatrace_azure_credentials", + "dynatrace_k8s_credentials", + "dynatrace_service_anomalies", + "dynatrace_application_anomalies", + "dynatrace_host_anomalies", + "dynatrace_database_anomalies", + "dynatrace_custom_anomalies", + "dynatrace_disk_anomalies", + # "dynatrace_calculated_service_metric", #issue -> bug: windows specific due to path length limit + "dynatrace_service_naming", + "dynatrace_host_naming", + "dynatrace_processgroup_naming", + # "dynatrace_slo", #issue -> bug: whitespace issue + "dynatrace_span_entry_point", + "dynatrace_span_capture_rule", + "dynatrace_span_context_propagation", + "dynatrace_resource_attributes", + "dynatrace_span_attribute", + "dynatrace_mobile_application", + # "dynatrace_credentials", #issue -> bug: unknown issue? not supported? + "dynatrace_browser_monitor", + "dynatrace_http_monitor", ] -# [AA 2021.12.13] Set default values -setdir = '.' - # [AA 2021.11.29] Arguments passed if(len(sys.argv) == 1): @@ -174,78 +173,113 @@ if(len(sys.argv) == 1): copy("main.tf", ".\\main.tf") # [AA, EW 2021.11.30] Copy configuration into each configration folder - copy("configuration.tf", - env['DYNATRACE_TARGET_FOLDER'] + "\\configuration.tf") + copy("configuration.tf", env['DYNATRACE_TARGET_FOLDER'] + "\\configuration.tf") + + # [AA 2021.01.14] Set initial values + isDeleted = False + iisDeleted = False + iiisDeleted = False # [AA, EW 2021.11.30] Iterate trough each folder in targetfolder, f could mean file OR folder - for dirname in os.listdir(env['DYNATRACE_TARGET_FOLDER']): - d, dflag = checkdir(os.path.join( - env['DYNATRACE_TARGET_FOLDER'], dirname)) - # print('[DEBUG]', 'd', d) + for envdirname in os.listdir(env['DYNATRACE_TARGET_FOLDER']): + path = os.path.join(env['DYNATRACE_TARGET_FOLDER'], envdirname) + isFolder, isDeleted = checkdir(path) - # [AA 2021.12.13] If the associated path is a directory - if dflag: + # [AA 2021.12.13] If the associated path is a folder, otherwise its the configuration.tf file + if isFolder is True and isDeleted is False: # [AA 2021.12.09] Create empty dictinary to store resource type and resource name myDict = {} - # [AA 2021.12.01] Loop through directory, eg: management_zones, autotags - for file in os.listdir(d): - setskipflag = False + # [AA 2021.12.01] Loop through folders, eg: management_zones, autotags + # [AA 2021.12.13] Check if there is another subdirectory or a file + for dirname in os.listdir(path): + ppath = os.path.join(path, dirname) + iisFolder, iisDeleted = checkdir(ppath) - # [AA 2021.12.13] Check if there is another subdirectory or a file - dd, ddflag = checkdir(os.path.join(d, file)) - # print('[DEBUG]', 'ddir', dd, 'ddflag', ddflag) - - # [AA 2021.12.14] If dd was not deleted and is a "sub"-directory - if os.path.exists(dd) and ddflag: - - # [AA 2021.12.13] Set directory - setdir = dd.replace(os.sep, '/') + # [AA 2021.12.14] If there is a "sub"-directory + if iisFolder is True and iisDeleted is False: # [AA 2021.12.13] Loop through that particular directory, where the files are! - for ffile in os.listdir(dd): - key, value = readfile(dd, ffile) + for ddirname in os.listdir(ppath): + + # [AA 2021.12.14] But check if there is another "sub"-"sub"-directory + pppath = os.path.join(ppath, ddirname) + iiisFolder, iiisDeleted = checkdir(pppath) + + # [AA 2021.12.14] If there is a "sub"-"sub"-directory + if iiisFolder is True and iiisDeleted is False: + for file in os.listdir(pppath): + filepath = os.path.join(pppath, file) + + # [AA 2022.01.13] Try to read file otherwise catch exception + try: + key, val = readfile(filepath) + myDict = addentry(key, val, myDict) + except UnicodeDecodeError: + print("File:", filepath) + + # [AA 2021.12.13] Set resource name + resource = pppath.replace(os.sep, '/') + + # [AA 2021.12.14] If there is not a "sub"-"sub"-directory + elif iiisFolder is False and iiisDeleted is False: + filepath = pppath + try: + key, val = readfile(filepath) + myDict = addentry(key, val, myDict) + except UnicodeDecodeError: + print("File:", filepath) + + # [AA 2021.12.13] Set resource name + resource = ppath.replace(os.sep, '/') + + # [AA 2021.12.14] Manual exception will be thrown + elif iiisFolder is True and iiisDeleted is True: + print("[DEBUG]", "Deleting and skipping empty folder %s" % (ppath if pppath is None else pppath)) + + # [AA 2021.12.14] If there is not a "sub"-directory + elif iisFolder is False and iisDeleted is False: + filepath = ppath + try: + key, val = readfile(filepath) myDict = addentry(key, val, myDict) + except UnicodeDecodeError: + print("File:", filepath) - # [AA 2021.12.14] If dd was not deleted and is not a directory (=a file) - elif os.path.exists(dd) and ddflag is False: - - # [AA 2021.12.13] Set directory - setdir = d.replace(os.sep, '/') - - # [AA 2021.12.13] There is no subdirectory, it is a file - # print("[DEBUG]", "Reading", "configDirFile", d) - key, val = readfile(d, file) - myDict = addentry(key, val, myDict) - - # [AA 2021.12.14] The directory was deleted - elif not os.path.exists(dd) and ddflag is False: - print("[DEBUG]", "Directory was deleted:", dd) - setskipflag = True - - # [AA 2021.12.14] Directory could not be deleted - elif not os.path.exists(dd) and ddflag is True: - print("[DEBUG]", "Deletion not successful:", dd) - setskipflag = True + # [AA 2021.12.13] Set resource name + resource = path.replace(os.sep, '/') # [AA 2021.12.14] Manual exception will be thrown - else: - print("[DEBUG]", "Uncaught Exception Error.") - setskipflag = True + elif iisFolder is True and iisDeleted is True: + print("[DEBUG]", "Empty folder deleted %s" % (path if ppath is None else ppath)) - if setskipflag is not True: + if isDeleted is False and iisDeleted is False and iiisDeleted is False: # [AA, EW 2021.11.30] Copy module - copy("module.tf", setdir + "\\module.tf") + copy("module.tf", resource + "\\module.tf") # [AA 2021.12.10] Print the amount of resources exported from Dynatrace - print("[DICTIONARY]", "resource", ", ".join(myDict.keys()), - "amount", "0" if myDict.get(key) is None else len(myDict.get(key)), "\n") + # [AA 2021.12.14] Multiple keys because they are stored withing the same subfolder + if len(myDict.keys()) > 1: + total = 0 + print("[DICTIONARY]") + for i, (key, value) in enumerate(myDict.items()): + print(" ", "resource", key, "amount", "0" if value is None else len(value)) + total += len(value) + print(" ", "total resources", i+1, "total files", total) + sys.stdout.write("\n") + else: + print("[DICTIONARY]", "resource", ", ".join(myDict.keys()), "total files", "0" if myDict.get(key) is None else len(myDict.get(key)), "\n") # [AA 2021.12.13] Append the necessary modules to the main.tf file - writefile(key, setdir) - # print("[DEBUG]", "Set directory:", setdir) + writefile(key, resource) + # print("[DEBUG]", "Set resource:", resource) + # [AA 2021.12.14] Manual exception will be thrown + elif isFolder is True and isDeleted is True: + print("[DEBUG]", "Empty folder deleted %s" % path) + + print("[DEBUG]", "Finished.") else: print("Usage example: ") print("python .\exportConfig.py") diff --git a/importConfig.py b/importStates.py similarity index 100% rename from importConfig.py rename to importStates.py diff --git a/main.tf b/main.tf index d041b3d..3b92832 100644 --- a/main.tf +++ b/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { dynatrace = { - version = "1.9.0" + version = "1.9.1" source = "dynatrace-oss/dynatrace" } } @@ -11,6 +11,15 @@ terraform { # source = "git::https://github.com/arnauagithub/DynatraceTerraformConfiguration.git?ref=20211130-151123" # } -module "dynatrace_alerting_profile" { source = "./configuration/20211222-154804_EUPROD/alerting_profiles" } -module "dynatrace_autotag" { source = "./configuration/20211222-154804_EUPROD/autotags" } -module "dynatrace_custom_service" { source = "./configuration/20211222-154804_EUPROD/custom_services" } \ No newline at end of file +module "dynatrace_alerting_profile" { source = "./output/20220117-083219_EMEA_PROD/alerting_profiles" } +module "dynatrace_service_anomalies" { source = "./output/20220117-083219_EMEA_PROD/anomalies" } +module "dynatrace_mobile_application" { source = "./output/20220117-083219_EMEA_PROD/applications/mobile" } +module "dynatrace_autotag" { source = "./output/20220117-083219_EMEA_PROD/autotags" } +module "dynatrace_k8s_credentials" { source = "./output/20220117-083219_EMEA_PROD/credentials" } +module "dynatrace_custom_service" { source = "./output/20220117-083219_EMEA_PROD/custom_services" } +module "dynatrace_dashboard" { source = "./output/20220117-083219_EMEA_PROD/dashboards" } +module "dynatrace_management_zone" { source = "./output/20220117-083219_EMEA_PROD/management_zones" } +module "dynatrace_service_naming" { source = "./output/20220117-083219_EMEA_PROD/naming/services" } +module "dynatrace_notification" { source = "./output/20220117-083219_EMEA_PROD/notifications" } +module "dynatrace_request_attribute" { source = "./output/20220117-083219_EMEA_PROD/request_attributes" } +module "dynatrace_http_monitor" { source = "./output/20220117-083219_EMEA_PROD/synthetic/http" } \ No newline at end of file diff --git a/templates/configuration.tf b/templates/configuration.tf index 0dd4b05..17cac3b 100644 --- a/templates/configuration.tf +++ b/templates/configuration.tf @@ -1,7 +1,7 @@ terraform { required_providers { dynatrace = { - version = "1.9.0" + version = "1.9.1" source = "dynatrace-oss/dynatrace" } } diff --git a/templates/main.tf b/templates/main.tf index 6a7e032..da9b57c 100644 --- a/templates/main.tf +++ b/templates/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { dynatrace = { - version = "1.9.0" + version = "1.9.1" source = "dynatrace-oss/dynatrace" } } diff --git a/templates/module.tf b/templates/module.tf index c4957c8..8877928 100644 --- a/templates/module.tf +++ b/templates/module.tf @@ -1,7 +1,7 @@ terraform { required_providers { dynatrace = { - version = "1.9.0" + version = "1.9.1" source = "dynatrace-oss/dynatrace" } }