-
Couldn't load subscription status.
- Fork 9
Create last_update_export.py #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
josephoaks
wants to merge
10
commits into
uyuni-project:main
Choose a base branch
from
josephoaks:patch-1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
813bfb1
Create last_update_export.py
josephoaks 4936104
Update last_update_export.py
josephoaks bea72fd
Update last_update_export.py
josephoaks cb0cd47
Update last_update_export.py
josephoaks 8279d56
Update uyuni-tools/last_update_export.py
josephoaks 63cf30f
Update last_update_export.py
josephoaks d55f838
Fixed the shebang line
josephoaks 366949d
Update last_update_export.py
josephoaks f70f8b9
Update last_update_export.py script header
josephoaks 5b3e496
Create initial_export.py
josephoaks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| """ | ||
| Description: This script is used for the initial export of channels for the | ||
| customer. This means if they are a new to SUMA, or an existing | ||
| user and we don't know when they got their last update from the | ||
| SCC, or they are getting a new channel for i.e. a new Service Pack. | ||
|
|
||
| This script automates the process of exporting channel data from a | ||
| specified directory, clearing the directory before use, and | ||
| handling file and user permissions. It reads channel names from a | ||
| specified text file and executes a sync/export command for each. | ||
|
|
||
| The script logs each export operation to a daily log file and | ||
| changes the ownership of the exported files to a specific user and | ||
| group. | ||
|
|
||
| Constants: | ||
| BASE_DIR - Base directory from which channels are exported. | ||
| RSYNC_USER - Username owning the exported files. | ||
| RSYNC_GROUP - Group owning the exported files. | ||
| LOG_DIR - Directory where logs are stored. | ||
| CHANNELS_FILE - Path to the file containing channel names, one | ||
| channel per line. | ||
| TODAY - Today's date, used for naming the log file. | ||
|
|
||
| Instructions: | ||
| 1. THIS SCRIPT REQUIRES THE USE OF THE `mgr-sync -s refresh` | ||
| CREDENTIALS FILE! | ||
| 2. Ensure paths for BASE_DIR, LOG_DIR, and CHANNELS_FILE are | ||
| correctly set according to your system configuration. | ||
| 3. Adjust RSYNC_USER and RSYNC_GROUP to match appropriate user | ||
| and group on your system. | ||
| 4. Ensure 'channels.txt' contains the list of channels to be | ||
| exported, one per line. | ||
| 5. Run this script with sufficient permissions to access and modify | ||
| the specified directories and files. | ||
| """ | ||
|
|
||
| import os | ||
| import shutil | ||
| import datetime | ||
| import subprocess | ||
| import configparser | ||
| import ssl | ||
| import socket | ||
| from xmlrpc.client import ServerProxy | ||
| import shlex | ||
|
|
||
| # Constants | ||
| BASE_DIR = "/mnt" | ||
| OUTPUT_DIR = os.path.join(BASE_DIR, "export/initial") | ||
| LOG_DIR = os.path.join(BASE_DIR, "logs") | ||
| SCRIPTS = os.path.join(BASE_DIR, "scripts") | ||
| RSYNC_USER = "rsyncuser" | ||
| RSYNC_GROUP = "users" | ||
| CHANNELS_FILE = os.path.join(SCRIPTS, "channels.txt") | ||
| TODAY = datetime.date.today().strftime("%Y-%m-%d") | ||
|
|
||
| def create_client(): | ||
| config_path = os.path.expanduser('/root/.mgr-sync') | ||
| config = configparser.ConfigParser() | ||
| with open(config_path, 'r') as f: | ||
| config.read_string('[DEFAULT]\n' + f.read()) | ||
| manager_login = config.get('DEFAULT', 'mgrsync.user') | ||
| manager_password = config.get('DEFAULT', 'mgrsync.password') | ||
| suma_fqdn = socket.getfqdn() | ||
| manager_url = f"https://{suma_fqdn}/rpc/api" | ||
| context = ssl.create_default_context() | ||
| client = ServerProxy(manager_url, context=context) | ||
| key = client.auth.login(manager_login, manager_password) | ||
| return client, key | ||
|
|
||
| def setup_directories(): | ||
| os.makedirs(LOG_DIR, exist_ok=True) | ||
| if os.path.exists(OUTPUT_DIR): | ||
| shutil.rmtree(OUTPUT_DIR) | ||
| os.makedirs(OUTPUT_DIR, exist_ok=True) | ||
|
|
||
| def setup_logging(): | ||
| log_file_path = os.path.join(LOG_DIR, f"{TODAY}-initial-export.log") | ||
| return log_file_path | ||
|
|
||
| def get_channels(): | ||
| """ | ||
| Check if channels.txt exists and is not empty, or use API call. | ||
| """ | ||
| if os.path.exists(CHANNELS_FILE) and os.path.getsize(CHANNELS_FILE) > 0: | ||
| with open(CHANNELS_FILE, 'r') as file: | ||
| channels = [line.strip() for line in file if line.strip()] | ||
| else: | ||
| client, key = create_client() | ||
| channel_data = client.channel.listVendorChannels(key) | ||
| channels = [channel['label'] for channel in channel_data if 'label' in channel] | ||
| return channels | ||
|
|
||
| # Initialize directories and logging | ||
| setup_directories() | ||
| log_file_path = setup_logging() | ||
|
|
||
| # Initialized channels list | ||
| channels = get_channels() | ||
|
|
||
| # Process channel exports | ||
| for channel in channels: | ||
| product_dir = os.path.join(OUTPUT_DIR, channel) | ||
| os.makedirs(product_dir, exist_ok=True) | ||
| options_dict = { | ||
| "outputDir": product_dir, | ||
| "logLevel": "error", | ||
| "orgLimit": "2" | ||
| } | ||
| options = ' '.join([f"--{opt}='{val}'" for opt, val in options_dict.items()]) | ||
| command = f"inter-server-sync export --channels='{channel}' {options}" | ||
| command_args = shlex.split(command) | ||
| with open(log_file_path, "a") as log_file: | ||
| subprocess.run(command_args, stdout=log_file, stderr=subprocess.STDOUT) | ||
| current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") | ||
| completion_message = f"{current_time} Export for channel {channel} completed.\n" | ||
| log_file.write(completion_message) | ||
|
|
||
| # Change ownership of the base directory recursively | ||
| subprocess.run(['chown', '-R', f'{RSYNC_USER}.{RSYNC_GROUP}', BASE_DIR], check=True) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| """ | ||
| Script Name: Last Update Export | ||
|
|
||
| Description: | ||
| This script automates the export of software channels using an XML-RPC | ||
| client to interface with a server for use in Airgapped Environments or | ||
| Disconnected SUMA. | ||
|
|
||
| It is designed to operate on a scheduled basis (daily), removing the | ||
| previous day's export data, and writing new export logs into designated | ||
| directories. | ||
|
|
||
| The concept behind the script is to use the API's to determine which | ||
| channels have been updated within the defined dates, and export only | ||
| those channels, as such, this creates a more streamlined export/import | ||
| for Disconnected or Airgapped systems. | ||
|
|
||
| Instructions: | ||
| 1. Ensure Python 3.x is installed on your system. | ||
| 2. This script must be run with root privileges to manage file | ||
| permissions and perform system-level operations. | ||
| 3. Before running the script, update the `/root/.mgr-sync` configuration | ||
| file with the correct manager login credentials using | ||
| `mgr-sync -s refresh` to create the credentials file. | ||
| 4. Customize the directory path, by default `/mnt` was chosen but this | ||
| could be any location you want, ensure the location has ample free | ||
| space. | ||
| 5. Customize the 'RSYNC_USER' and 'RSYNC_GROUP' in the script to match | ||
| the user and group names on your system. | ||
| 6. Schedule this script using a cron job or another scheduler for daily | ||
| execution. | ||
|
|
||
| Intended Usage: | ||
| - Scheduled daily exports of software channel data. | ||
| - Logging of export operations in '/mnt/logs'. | ||
| - Exports are stored in '/mnt/export/updates'. | ||
| - This script is intended for systems administrators managing software | ||
| channel updates for Airgapped Environments. | ||
|
|
||
| Ensure the server and paths are correctly configured and accessible before running the script. | ||
| """ | ||
|
|
||
| import os | ||
| import subprocess | ||
| import datetime | ||
| from xmlrpc.client import ServerProxy | ||
| import ssl | ||
| import socket | ||
| import configparser | ||
| import shutil | ||
| import shlex | ||
|
|
||
| # Configuration Variables | ||
| BASE_DIR = "/mnt" # Define base directory where the exports will be. | ||
| OUTPUT_DIR = os.path.join(BASE_DIR, "export/updates") | ||
| LOG_DIR = os.path.join(BASE_DIR, "logs") | ||
| TODAY = datetime.date.today() | ||
| TARGET_DATE = TODAY - datetime.timedelta(days=1) # Define the number of days back for export, 1 day by default | ||
| RSYNC_USER = "rsyncuser" # Define rsync user | ||
| RSYNC_GROUP = "users" # Define rsync group | ||
|
|
||
| def setup_directories(base_path, output_path, log_path): | ||
| if not os.path.exists(base_path): | ||
| os.makedirs(base_path, exist_ok=True) | ||
| if not os.path.exists(log_path): | ||
| os.makedirs(log_path, exist_ok=True) | ||
| if os.path.exists(output_path): | ||
| shutil.rmtree(output_path) | ||
| os.makedirs(output_path, exist_ok=True) | ||
|
|
||
| def setup_logging(LOG_DIR, TODAY): | ||
| log_file_path = os.path.join(LOG_DIR, f"{TODAY}-daily_export.log") | ||
| return log_file_path | ||
|
|
||
| def create_client(): | ||
| config_path = os.path.expanduser('/root/.mgr-sync') | ||
| config = configparser.ConfigParser() | ||
| with open(config_path, 'r') as f: | ||
| config.read_string('[DEFAULT]\n' + f.read()) | ||
| manager_login = config.get('DEFAULT', 'mgrsync.user') | ||
| manager_password = config.get('DEFAULT', 'mgrsync.password') | ||
| suma_fqdn = socket.getfqdn() | ||
| manager_url = f"https://{suma_fqdn}/rpc/api" | ||
| context = ssl.create_default_context() | ||
| client = ServerProxy(manager_url, context=context) | ||
| return client, client.auth.login(manager_login, manager_password) | ||
|
|
||
| # Setup Directories and Logging | ||
| setup_directories(BASE_DIR, OUTPUT_DIR, LOG_DIR) | ||
| log_file_path = setup_logging(LOG_DIR, TODAY) | ||
|
|
||
| # Create XML-RPC Client | ||
| client, key = create_client() | ||
|
|
||
| # Process channels | ||
| channel_list = client.channel.listVendorChannels(key) | ||
| for channel in channel_list: | ||
| build_date, channel_label = client.channel.software.getChannelLastBuildById(key, channel["id"]).split()[0], channel["label"] | ||
| build_date = datetime.datetime.strptime(build_date, "%Y-%m-%d").date() | ||
|
|
||
| if TARGET_DATE <= build_date <= TODAY: | ||
| channel_OUTPUT_DIR = os.path.join(OUTPUT_DIR, channel_label) | ||
| os.makedirs(channel_OUTPUT_DIR, exist_ok=True) | ||
| options_dict = { | ||
| "outputDir": channel_OUTPUT_DIR, | ||
| "orgLimit": "2", # Define the default Organization, 2 by default assuming there is only 1, if multiple set this to the one you assign for exporting. | ||
| "logLevel": "error", # Set as 'error' by default but change to 'debug' for detailed logging | ||
| "packagesOnlyAfter": TARGET_DATE.strftime('%Y-%m-%d') | ||
| } | ||
| options = ' '.join([f"--{opt}='{val}'" for opt, val in options_dict.items()]) | ||
| command = f"inter-server-sync export --channels='{channel_label}' {options}" | ||
| command_args = shlex.split(command) | ||
| with open(log_file_path, "a") as log_file: | ||
| subprocess.run(command_args, stdout=log_file, stderr=subprocess.STDOUT, check=True) | ||
| current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") | ||
| completion_message = f"{current_time} Export for channel {channel_label} completed.\n" | ||
| log_file.write(completion_message) | ||
|
|
||
| # Change ownership of the output directory | ||
| subprocess.run(["chown", "-R", f"{RSYNC_USER}:{RSYNC_GROUP}", OUTPUT_DIR], check=True) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.