|
1 |
| -"""Base classes and options for the distros.""" |
| 1 | +"""Package to manage distribution download.""" |
2 | 2 |
|
3 | 3 | # Copyright (c) 2016 Alvaro Lopez Garcia
|
4 | 4 |
|
|
14 | 14 | # License for the specific language governing permissions and limitations
|
15 | 15 | # under the License.
|
16 | 16 |
|
17 |
| -import abc |
18 |
| -import hashlib |
19 |
| -import os |
20 |
| -import tempfile |
| 17 | +import itertools |
21 | 18 |
|
22 | 19 | from oslo_config import cfg
|
23 | 20 | from oslo_log import log
|
24 |
| -import requests |
25 |
| -import six |
26 |
| -import stevedore |
27 |
| - |
28 |
| -from imgsync import exception |
29 |
| -from imgsync import glance |
30 |
| - |
31 |
| -SUPPORTED_DISTROS = [ |
32 |
| - "centos6", |
33 |
| - "centos7", |
34 |
| - "ubuntu14", |
35 |
| - "ubuntu16", |
36 |
| - "ubuntu18", |
37 |
| - "ubuntu20", |
38 |
| - "ubuntu22", |
39 |
| - "debian10", |
40 |
| - "debian11", |
41 |
| - "debian12", |
42 |
| -] |
| 21 | + |
| 22 | +from imgsync.distros import debian |
| 23 | +from imgsync.distros import ubuntu |
| 24 | + |
| 25 | + |
| 26 | +_DISTRO_OBJS = { |
| 27 | + i.name: i |
| 28 | + for i in itertools.chain( |
| 29 | + ubuntu.Ubuntu.__subclasses__(), debian.Debian.__subclasses__() |
| 30 | + ) |
| 31 | +} |
| 32 | + |
| 33 | +SUPPORTED_DISTROS = list(_DISTRO_OBJS.keys()) |
43 | 34 |
|
44 | 35 | opts = [
|
45 | 36 | cfg.StrOpt(
|
|
66 | 57 | ),
|
67 | 58 | ]
|
68 | 59 |
|
| 60 | +cli_opts = [ |
| 61 | + cfg.BoolOpt( |
| 62 | + "download-only", |
| 63 | + default=False, |
| 64 | + help="Only download the images, do not sync them to glance. Be aware" |
| 65 | + " that images are deleted after being synced to glance, so if you use" |
| 66 | + " this option the images will be deleted after the download. Use this" |
| 67 | + " only for debugging purposes.", |
| 68 | + ), |
| 69 | + cfg.BoolOpt( |
| 70 | + "dry-run", |
| 71 | + default=False, |
| 72 | + help="Do not sync the images, only show what would be done. This still" |
| 73 | + " equires authenticating with Glance, in order to check what would be done.", |
| 74 | + ), |
| 75 | +] |
| 76 | + |
| 77 | + |
69 | 78 | CONF = cfg.CONF
|
70 | 79 | CONF.register_opts(opts)
|
| 80 | +CONF.register_cli_opts(cli_opts) |
71 | 81 |
|
72 | 82 | LOG = log.getLogger(__name__)
|
73 | 83 |
|
74 | 84 |
|
75 |
| -@six.add_metaclass(abc.ABCMeta) |
76 |
| -class BaseDistro(object): |
77 |
| - """Base class for all distributions.""" |
78 |
| - |
79 |
| - url = None |
80 |
| - |
81 |
| - def __init__(self): |
82 |
| - """Initialize the BaseDistro object.""" |
83 |
| - self.glance = glance.GLANCE |
84 |
| - |
85 |
| - @abc.abstractproperty |
86 |
| - def what(self): |
87 |
| - """Get what to sync. This has to be implemented by the child class.""" |
88 |
| - return None |
89 |
| - |
90 |
| - def sync(self): |
91 |
| - """Sync the images, calling the method that is needed.""" |
92 |
| - if self.what == "all": |
93 |
| - self._sync_all() |
94 |
| - elif self.what == "latest": |
95 |
| - self._sync_latest() |
96 |
| - else: |
97 |
| - LOG.warn("Nothing to do") |
98 |
| - |
99 |
| - def _get_file_checksum(self, path, block_size=2**20): |
100 |
| - """Get the checksum of a file. |
101 |
| -
|
102 |
| - Get the checksum of a file using sha512. |
103 |
| -
|
104 |
| - :param path: the path to the file |
105 |
| - :param block_size: block size to use when reading the file |
106 |
| - :returns: sha512 object |
107 |
| - """ |
108 |
| - sha512 = hashlib.sha512() |
109 |
| - with open(path, "rb") as f: |
110 |
| - buf = f.read(block_size) |
111 |
| - while len(buf) > 0: |
112 |
| - sha512.update(buf) |
113 |
| - buf = f.read(block_size) |
114 |
| - return sha512 |
115 |
| - |
116 |
| - def verify_checksum( |
117 |
| - self, |
118 |
| - location, |
119 |
| - name, |
120 |
| - checksum, |
121 |
| - ): |
122 |
| - """Verify the image's checksum.""" |
123 |
| - # TODO(aloga): not implemented yet |
124 |
| - |
125 |
| - def _download_one(self, url, checksum): |
126 |
| - """Download a file. |
127 |
| -
|
128 |
| - Download a file from a url and return a temporary file object. |
129 |
| -
|
130 |
| - :param url: the url to download |
131 |
| - :param checksum: tuple in the form (checksum_name, checksum_value) |
132 |
| - :returns: temporary file object |
133 |
| - """ |
134 |
| - with tempfile.NamedTemporaryFile(suffix=".imgsync", delete=False) as location: |
135 |
| - try: |
136 |
| - response = requests.get(url, stream=True, timeout=10) |
137 |
| - except Exception as e: |
138 |
| - os.remove(location.name) |
139 |
| - LOG.error(e) |
140 |
| - raise exception.ImageDownloadFailed(code=e.errno, reason=e.message) |
141 |
| - |
142 |
| - if not response.ok: |
143 |
| - os.remove(location.name) |
144 |
| - LOG.error( |
145 |
| - "Cannot download image: (%s) %s", |
146 |
| - response.status_code, |
147 |
| - response.reason, |
148 |
| - ) |
149 |
| - raise exception.ImageDownloadFailed( |
150 |
| - code=response.status_code, reason=response.reason |
151 |
| - ) |
152 |
| - |
153 |
| - for block in response.iter_content(1024): |
154 |
| - if block: |
155 |
| - location.write(block) |
156 |
| - location.flush() |
157 |
| - |
158 |
| - checksum_map = {"sha512": hashlib.sha512, "sha256": hashlib.sha256} |
159 |
| - sha = checksum_map.get(checksum[0])() |
160 |
| - block_size = 2**20 |
161 |
| - with open(location.name, "rb") as f: |
162 |
| - buf = f.read(block_size) |
163 |
| - while len(buf) > 0: |
164 |
| - sha.update(buf) |
165 |
| - buf = f.read(block_size) |
166 |
| - |
167 |
| - if sha.hexdigest() != checksum[1]: |
168 |
| - os.remove(location.name) |
169 |
| - e = exception.ImageVerificationFailed( |
170 |
| - url=url, expected=checksum, obtained=sha.hexdigest() |
171 |
| - ) |
172 |
| - LOG.error(e) |
173 |
| - raise e |
174 |
| - |
175 |
| - LOG.info("Image '%s' downloaded", url) |
176 |
| - return location |
177 |
| - |
178 |
| - |
179 | 85 | class DistroManager(object):
|
180 | 86 | """Class to manage the distributions."""
|
181 | 87 |
|
182 | 88 | def __init__(self):
|
183 | 89 | """Initialize the DistroManager object."""
|
184 |
| - self.distros = stevedore.NamedExtensionManager( |
185 |
| - "imgsync.distros", |
186 |
| - CONF.distributions, |
187 |
| - invoke_on_load=True, |
188 |
| - propagate_map_exceptions=True, |
189 |
| - ) |
| 90 | + self.distros = [] |
| 91 | + |
| 92 | + for distro in CONF.distributions: |
| 93 | + if distro not in SUPPORTED_DISTROS: |
| 94 | + raise ValueError("Unsupported distribution %s" % distro) |
| 95 | + self.distros.append(_DISTRO_OBJS[distro]()) |
190 | 96 |
|
191 | 97 | def sync(self):
|
192 | 98 | """Sync the distributions."""
|
193 |
| - LOG.info("Syncing %s", self.distros.names()) |
194 |
| - self.distros.map_method("sync") |
| 99 | + if CONF.download_only: |
| 100 | + LOG.warn("Only downloading the images, not checkinf if they need sync.") |
| 101 | + |
| 102 | + if CONF.dry_run: |
| 103 | + LOG.warn("Dry run, not syncing the images to glance.") |
| 104 | + |
| 105 | + for distro in self.distros: |
| 106 | + LOG.info("Syncing %s", distro.name) |
| 107 | + distro.sync() |
0 commit comments