adding-apt-update-functionality

* adding apt tool to update package

Closes-bug: #1614600
Co-Authored-By: Anastasiya Tolochkova <atolochkova@mirantis.com>
Co-Authored-By: Ilya Kharin <akscram@gmail.com>
Change-Id: I6651055e798c01e22b9ba2b6fe573bb2abf7231c
This commit is contained in:
Alexis Rivera De La Torre (gardlt) 2016-08-17 11:37:29 -05:00 committed by Anastasiya
parent 183ed45496
commit 12611efbc8
2 changed files with 228 additions and 0 deletions

136
octane/tests/test_apt.py Normal file
View File

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# 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
#
# 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.
import pytest
from octane.util import apt
RELEASES = [
"""\
Origin: Mirantis
Label: mos9.0
Suite: mos9.0-updates
Codename: mos9.0-updates
MD5Sum: """,
"""\
Origin: Ubuntu
Label:Ubuntu
Suite: vivid
Version: 15.04
Codename: vivid
SHA1:""",
"""\
SHA256:""",
]
PARAMS = [
{'origin': 'Mirantis',
'label': 'mos9.0',
'codename': 'mos9.0-updates',
'suite': 'mos9.0-updates'},
{'origin': 'Ubuntu',
'label': 'Ubuntu',
'suite': 'vivid',
'codename': 'vivid',
'version': '15.04'},
]
REPOS = [
{'uri': 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/',
'suite': 'mos9.0-updates',
'type': 'deb',
'section': 'main',
'name': 'mos-updates',
'priority': 1050},
{'uri': 'http://us.archive.ubuntu.com/ubuntu/',
'suite': 'vivid',
'type': 'deb-src',
'section': 'main universe',
'name': 'ubuntu',
'priority': 601},
]
@pytest.mark.parametrize('repo,release,expected_params,url,code,is_error', [
(REPOS[0], RELEASES[0], PARAMS[0],
'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/dists/'
'mos9.0-updates/Release',
200, False),
(REPOS[1], RELEASES[1], PARAMS[1],
'http://us.archive.ubuntu.com/ubuntu/dists/vivid/Release', 200, False),
(REPOS[1], RELEASES[2], {},
'http://us.archive.ubuntu.com/ubuntu/dists/vivid/Release', 200, False),
({'uri': 'https://example.com', 'suite': 'none'}, '', None,
'https://example.com/dists/none/Release', 300, True),
({'uri': 'http://example.com/ubuntu', 'suite': 'trusty'}, '', None,
'https://example.com/ubuntu/dists/none/Release', 300, True),
])
def test_fetch_release_parameters(mocker, repo, release, expected_params, url,
code, is_error):
mock_urlopen = mocker.patch('six.moves.urllib.request.urlopen')
resp = mocker.MagicMock(code=code)
resp.__iter__.return_value = iter(release.splitlines(True))
mock_urlopen.return_value = resp
if not is_error:
params = apt.fetch_release_parameters(repo)
mock_urlopen.assert_called_once_with(url)
assert params == expected_params
else:
with pytest.raises(apt.UnavailableRelease):
apt.fetch_release_parameters(repo)
@pytest.mark.parametrize('repo,filename,content', [
(REPOS[0], '/etc/apt/sources.list.d/mos-updates.list',
'deb http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/ mos9.0-updates '
'main'),
(REPOS[1], '/etc/apt/sources.list.d/ubuntu.list',
'deb-src http://us.archive.ubuntu.com/ubuntu/ vivid main universe'),
])
def test_create_repo_source(repo, filename, content):
result = apt.create_repo_source(repo)
assert result[0] == filename
assert result[1] == content
PREFERENCES = [
"""\
Package: *
Pin: release a=mos9.0-updates,o=Mirantis,n=mos9.0-updates,l=mos9.0,c=main
Pin-Priority: 1050""",
"""\
Package: *
Pin: release a=vivid,o=Ubuntu,n=vivid,l=Ubuntu,c=main,v=15.04
Pin-Priority: 601
Package: *
Pin: release a=vivid,o=Ubuntu,n=vivid,l=Ubuntu,c=universe,v=15.04
Pin-Priority: 601""",
]
@pytest.mark.parametrize('params,repo,filename,content', [
(PARAMS[0], REPOS[0], '/etc/apt/preferences.d/mos-updates.pref',
PREFERENCES[0]),
(PARAMS[1], REPOS[1], '/etc/apt/preferences.d/ubuntu.pref',
PREFERENCES[1]),
])
def test_create_repo_preferences(mocker, params, repo, filename, content):
mock_fetch_release = mocker.patch(
'octane.util.apt.fetch_release_parameters')
mock_fetch_release.return_value = params
result = apt.create_repo_preferences(repo)
assert result[0] == filename
assert result[1] == content

92
octane/util/apt.py Normal file
View File

@ -0,0 +1,92 @@
# 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
#
# 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.
from six.moves import urllib
from octane.util import ssh as ssh_util
PREFERENCES = [
('suite', 'a'),
('origin', 'o'),
('codename', 'n'),
('label', 'l'),
('component', 'c'),
('version', 'v'),
]
class UnavailableRelease(Exception):
message = "Unexpected response status {0} for URL {1}."
def __init__(self, status, url):
super(UnavailableRelease, self).__init__(self.message.format(
status, url))
def upgrade_packages(node, packages, fix_broken=False):
pkgs = ' '.join(packages)
update_cmd = ['sh', '-c',
'DEBIAN_FRONTEND=noninteractive apt-get '
'install --only-upgrade --yes --force-yes '
'-o Dpkg::Options::="--force-confdef" '
'-o Dpkg::Options::="--force-confold" '
'{0}'.format(pkgs)]
return ssh_util.call(update_cmd, node=node)
def fetch_release_parameters(repo):
url_release_part = "./{0}".format('/'.join(('dists', repo['suite'],
'Release')))
base_url = repo['uri']
if not base_url.endswith('/'):
base_url = "{0}/".format(repo['uri'])
release_url = urllib.parse.urljoin(base_url, url_release_part)
resp = urllib.request.urlopen(release_url)
if resp.code != 200:
raise UnavailableRelease(resp.code, release_url)
params = {}
for line in resp:
key, _, value = line.partition(':')
key = key.strip().lower()
value = value.strip()
if not key or not value:
continue
# NOTE(akscram): Normal Release files contain meaningful fields
# at the beginning.
if key in ('md5sum', 'sha1', 'sha256'):
break
params[key] = value
return params
def create_repo_source(repo):
filename = "/etc/apt/sources.list.d/{0}.list".format(repo['name'])
content = "{type} {uri} {suite} {section}".format(**repo)
return filename, content
def create_repo_preferences(repo):
filename = "/etc/apt/preferences.d/{0}.pref".format(repo['name'])
release_params = fetch_release_parameters(repo)
content = []
components = repo['section'].split()
for component in components:
params = dict(release_params, component=component)
release = ','.join("{0}={1}".format(key, params[name])
for name, key in PREFERENCES
if name in params)
content.append(
"Package: *\n"
"Pin: release {release}\n"
"Pin-Priority: {priority}"
.format(release=release, priority=repo['priority']))
return filename, '\n\n'.join(content)