setup_offline_xcpng_repos 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env python
  2. from __future__ import print_function, unicode_literals
  3. import argparse
  4. import io # Ensures consistent file handling in python2 and python3
  5. import logging
  6. import os
  7. import re
  8. import tarfile
  9. import shutil # For removing directories
  10. import subprocess
  11. import sys
  12. try:
  13. from subprocess import DEVNULL # python 3
  14. except ImportError:
  15. DEVNULL = open(os.devnull, 'wb') # python 2
  16. # Configure logging
  17. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  18. REPOS_DIR = "/var/local/xcp-ng-repos"
  19. # Parse command-line argument
  20. parser = argparse.ArgumentParser(description="Setup offline repositories.")
  21. parser.add_argument("archive_path", help="Path to the tar archive file")
  22. args = parser.parse_args()
  23. archive_path = args.archive_path
  24. # Check if file exists
  25. if not os.path.exists(archive_path):
  26. logging.error("The specified file does not exist.")
  27. sys.exit(1)
  28. # Check if file is a tar archive
  29. if not tarfile.is_tarfile(archive_path):
  30. logging.error("The file is not a valid tar archive.")
  31. sys.exit(1)
  32. # Check if tar archive is corrupted
  33. try:
  34. with tarfile.open(archive_path, 'r') as tar:
  35. tar.getmembers() # Attempt to read the contents to detect corruption
  36. logging.info("Tar archive seems valid.")
  37. except tarfile.TarError:
  38. logging.error("The tar archive is corrupted.")
  39. sys.exit(1)
  40. # Check for repository paths
  41. reponames = []
  42. with tarfile.open(archive_path, 'r') as tar:
  43. for member in tar.getnames():
  44. if re.match(r'^[a-zA-Z0-9_-]+/x86_64/repodata$', member):
  45. reponames.append(member.split('/')[0])
  46. if not reponames:
  47. logging.error("No valid repository paths found in the tar archive.")
  48. sys.exit(1)
  49. # Process each repository path
  50. for reponame in reponames:
  51. logging.info("Processing repo: %s", reponame)
  52. repo_path = os.path.join(REPOS_DIR, reponame)
  53. # Remove existing reponame directory if it exists
  54. if os.path.exists(repo_path):
  55. logging.info("Removing existing directory: %s", repo_path)
  56. shutil.rmtree(repo_path)
  57. # Create REPOS_DIR if it doesn't exist
  58. if not os.path.exists(REPOS_DIR):
  59. logging.info("Creating directory: %s", REPOS_DIR)
  60. os.makedirs(REPOS_DIR)
  61. # Extract the reponame directory from the tar archive
  62. logging.info("Extracting archive...")
  63. with tarfile.open(archive_path, 'r') as tar:
  64. members_to_extract = [member for member in tar.getmembers() if member.name.startswith(reponame + '/')]
  65. tar.extractall(path=REPOS_DIR, members=members_to_extract)
  66. # Fix the owner and group
  67. subprocess.check_call(['chown', 'root.root', repo_path, '-R'])
  68. logging.info("Extracted %s to %s", reponame, REPOS_DIR)
  69. # Determine the repository file to use
  70. if 'linstor' in reponame:
  71. repofilename = '/etc/yum.repos.d/xcp-ng-linstor.repo'
  72. else:
  73. repofilename = '/etc/yum.repos.d/xcp-ng.repo'
  74. # Check and update the repository file
  75. content = ""
  76. if os.path.exists(repofilename):
  77. with io.open(repofilename, 'r', encoding='utf-8') as f:
  78. content = f.read()
  79. if "# --- OFFLINE UPDATES v1 ---" not in content:
  80. logging.info("Deleting existing repository file: %s", repofilename)
  81. os.remove(repofilename)
  82. content = "" # Reset content after deletion
  83. # Create or update repository file
  84. if not os.path.exists(repofilename):
  85. logging.info("Creating repository file: %s", repofilename)
  86. with io.open(repofilename, 'w', encoding='utf-8') as f:
  87. f.write("# --- OFFLINE UPDATES v1 --- DO NOT DELETE THIS LINE\n")
  88. # Refresh content after creation
  89. with io.open(repofilename, 'r', encoding='utf-8') as f:
  90. content = f.read()
  91. reponame_yum = 'xcp-ng-%s' % reponame
  92. reponame_section = '[%s]' % reponame_yum
  93. baseurl_line = 'baseurl=file://%s/%s/x86_64/' % (REPOS_DIR, reponame)
  94. name_line = 'name=XCP-ng Offline %s Repository' % reponame.capitalize()
  95. if reponame_section in content:
  96. if baseurl_line not in content:
  97. # Use `yum-config-manager` to set or update the baseurl
  98. try:
  99. logging.info("Setting baseurl for %s to %s", reponame_section, baseurl_line)
  100. subprocess.check_call(
  101. [
  102. 'yum-config-manager',
  103. '--setopt',
  104. '%s.%s' % (reponame_yum, baseurl_line),
  105. '--save'
  106. ],
  107. stdout=DEVNULL
  108. )
  109. except subprocess.CalledProcessError as e:
  110. logging.error("Failed to set baseurl for %s. Details: %s", reponame_section, e)
  111. exit(1)
  112. else:
  113. logging.info("Appending repository definition for %s to %s", reponame_section, repofilename)
  114. with io.open(repofilename, 'a', encoding='utf-8') as file:
  115. file.write("""
  116. %s
  117. %s
  118. %s
  119. enabled=1
  120. gpgcheck=1
  121. repo_gpgcheck=1
  122. gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-xcpng
  123. """ % (reponame_section, name_line, baseurl_line))
  124. # Delete yum cache
  125. try:
  126. yum_cache = '/var/cache/yum'
  127. if os.path.exists(yum_cache):
  128. logging.info("Deleting yum cache: %s", yum_cache)
  129. subprocess.check_call(['rm', '-r', yum_cache])
  130. except subprocess.CalledProcessError as e:
  131. logging.error("Failed to clear yum cache. Details: %s", e)
  132. exit(1)