lftools-uv 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lftools_uv/__init__.py +44 -0
- lftools_uv/api/__init__.py +10 -0
- lftools_uv/api/client.py +92 -0
- lftools_uv/api/endpoints/__init__.py +10 -0
- lftools_uv/api/endpoints/gerrit.py +433 -0
- lftools_uv/api/endpoints/nexus2.py +301 -0
- lftools_uv/api/endpoints/nexus3.py +408 -0
- lftools_uv/api/endpoints/readthedocs.py +269 -0
- lftools_uv/api/exceptions.py +19 -0
- lftools_uv/cli/__init__.py +125 -0
- lftools_uv/cli/config.py +63 -0
- lftools_uv/cli/dco.py +89 -0
- lftools_uv/cli/deploy.py +329 -0
- lftools_uv/cli/gerrit.py +233 -0
- lftools_uv/cli/github_cli.py +282 -0
- lftools_uv/cli/infofile.py +354 -0
- lftools_uv/cli/jenkins/__init__.py +244 -0
- lftools_uv/cli/jenkins/builds.py +53 -0
- lftools_uv/cli/jenkins/jobs.py +68 -0
- lftools_uv/cli/jenkins/nodes.py +42 -0
- lftools_uv/cli/jenkins/plugins.py +188 -0
- lftools_uv/cli/jenkins/token.py +176 -0
- lftools_uv/cli/ldap_cli.py +187 -0
- lftools_uv/cli/lfidapi.py +86 -0
- lftools_uv/cli/license.py +64 -0
- lftools_uv/cli/nexus.py +236 -0
- lftools_uv/cli/nexus2/__init__.py +39 -0
- lftools_uv/cli/nexus2/privilege.py +58 -0
- lftools_uv/cli/nexus2/repository.py +61 -0
- lftools_uv/cli/nexus2/role.py +60 -0
- lftools_uv/cli/nexus2/user.py +61 -0
- lftools_uv/cli/nexus3/__init__.py +46 -0
- lftools_uv/cli/nexus3/asset.py +55 -0
- lftools_uv/cli/nexus3/privilege.py +36 -0
- lftools_uv/cli/nexus3/repository.py +36 -0
- lftools_uv/cli/nexus3/role.py +50 -0
- lftools_uv/cli/nexus3/script.py +87 -0
- lftools_uv/cli/nexus3/tag.py +67 -0
- lftools_uv/cli/nexus3/task.py +41 -0
- lftools_uv/cli/nexus3/user.py +74 -0
- lftools_uv/cli/no_cmd.py +26 -0
- lftools_uv/cli/rtd.py +223 -0
- lftools_uv/cli/schema.py +40 -0
- lftools_uv/cli/sign.py +135 -0
- lftools_uv/cli/utils.py +43 -0
- lftools_uv/cli/version.py +110 -0
- lftools_uv/config.py +64 -0
- lftools_uv/conftest.py +9 -0
- lftools_uv/deploy.py +945 -0
- lftools_uv/git/__init__.py +10 -0
- lftools_uv/git/gerrit.py +280 -0
- lftools_uv/git/templates/config-params.yaml +3 -0
- lftools_uv/git/templates/gitreview +5 -0
- lftools_uv/git/templates/project.yaml +15 -0
- lftools_uv/git/templates/serverCredentialMappings.yaml +18 -0
- lftools_uv/github_helper.py +230 -0
- lftools_uv/helpers.py +21 -0
- lftools_uv/jenkins/__init__.py +73 -0
- lftools_uv/jenkins/token.py +52 -0
- lftools_uv/ldap_cli.py +21 -0
- lftools_uv/lfidapi.py +212 -0
- lftools_uv/license.py +81 -0
- lftools_uv/nexus/__init__.py +332 -0
- lftools_uv/nexus/cmd.py +544 -0
- lftools_uv/nexus/release_docker_hub.py +919 -0
- lftools_uv/nexus/util.py +34 -0
- lftools_uv/oauth2_helper.py +42 -0
- lftools_uv/openstack/__init__.py +12 -0
- lftools_uv/openstack/cmd.py +256 -0
- lftools_uv/openstack/image.py +241 -0
- lftools_uv/openstack/no_cmd.py +26 -0
- lftools_uv/openstack/object.py +25 -0
- lftools_uv/openstack/server.py +95 -0
- lftools_uv/openstack/stack.py +210 -0
- lftools_uv/openstack/volume.py +98 -0
- lftools_uv/schema.py +46 -0
- lftools_uv/shell/__init__.py +11 -0
- lftools_uv/shell/autocorrectinfofile.py +42 -0
- lftools_uv/shell/dco.py +144 -0
- lftools_uv/shell/deploy.py +42 -0
- lftools_uv/shell/fix_yamllint.py +42 -0
- lftools_uv/shell/gerrit_create.py +42 -0
- lftools_uv/shell/inactivecommitters.py +42 -0
- lftools_uv/shell/sign.py +42 -0
- lftools_uv/shell/version.py +42 -0
- lftools_uv/shell/yaml4info.py +42 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/autocorrectinfofile +169 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/deploy +850 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/fix_yamllint +129 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/gerrit_create +253 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/inactivecommitters +108 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/sign +331 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/version +176 -0
- lftools_uv-0.1.0.data/data/share/lftools-uv/shell/yaml4info +64 -0
- lftools_uv-0.1.0.dist-info/METADATA +271 -0
- lftools_uv-0.1.0.dist-info/RECORD +98 -0
- lftools_uv-0.1.0.dist-info/WHEEL +4 -0
- lftools_uv-0.1.0.dist-info/entry_points.txt +10 -0
lftools_uv/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SPDX-License-Identifier: EPL-1.0
|
|
2
|
+
##############################################################################
|
|
3
|
+
# Copyright (c) 2017, 2023 The Linux Foundation and others.
|
|
4
|
+
#
|
|
5
|
+
# All rights reserved. This program and the accompanying materials
|
|
6
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
7
|
+
# which accompanies this distribution, and is available at
|
|
8
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
9
|
+
#
|
|
10
|
+
# Contributors:
|
|
11
|
+
# Thanh Ha - Initial implementation
|
|
12
|
+
# Linux Foundation Release Engineering
|
|
13
|
+
##############################################################################
|
|
14
|
+
"""lftools-uv package."""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
__author__ = "Thanh Ha"
|
|
18
|
+
__summary__ = "Linux Foundation Release Engineering Tools"
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LogFormatter(logging.Formatter):
|
|
25
|
+
"""Custom log formatter."""
|
|
26
|
+
|
|
27
|
+
default_fmt: logging.Formatter = logging.Formatter("%(levelname)s: %(message)s")
|
|
28
|
+
debug_fmt: logging.Formatter = logging.Formatter("%(levelname)s: %(name)s:%(lineno)d: %(message)s")
|
|
29
|
+
info_fmt: logging.Formatter = logging.Formatter("%(message)s")
|
|
30
|
+
|
|
31
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
32
|
+
"""Format log messages depending on log level."""
|
|
33
|
+
if record.levelno == logging.INFO:
|
|
34
|
+
return self.info_fmt.format(record)
|
|
35
|
+
if record.levelno == logging.DEBUG:
|
|
36
|
+
return self.debug_fmt.format(record)
|
|
37
|
+
else:
|
|
38
|
+
return self.default_fmt.format(record)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
42
|
+
console_handler.setFormatter(LogFormatter())
|
|
43
|
+
logging.getLogger("").setLevel(logging.INFO)
|
|
44
|
+
logging.getLogger("").addHandler(console_handler)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# SPDX-License-Identifier: EPL-1.0
|
|
2
|
+
##############################################################################
|
|
3
|
+
# Copyright (c) 2019 The Linux Foundation and others.
|
|
4
|
+
#
|
|
5
|
+
# All rights reserved. This program and the accompanying materials
|
|
6
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
7
|
+
# which accompanies this distribution, and is available at
|
|
8
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
9
|
+
##############################################################################
|
|
10
|
+
"""Initializes the REST API client."""
|
lftools_uv/api/client.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# SPDX-License-Identifier: EPL-1.0
|
|
2
|
+
##############################################################################
|
|
3
|
+
# Copyright (c) 2019, 2023 The Linux Foundation and others.
|
|
4
|
+
#
|
|
5
|
+
# All rights reserved. This program and the accompanying materials
|
|
6
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
7
|
+
# which accompanies this distribution, and is available at
|
|
8
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
9
|
+
##############################################################################
|
|
10
|
+
"""REST API interface using Requests."""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from typing import Any, Dict, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RestApi(object):
|
|
20
|
+
"""A generic REST API interface."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, **kwargs: Dict[str, str]) -> None:
|
|
23
|
+
"""Initialize the REST API class."""
|
|
24
|
+
self.params: Dict[str, Dict[str, str]] = kwargs
|
|
25
|
+
|
|
26
|
+
if kwargs["creds"]:
|
|
27
|
+
self.creds: Dict[str, str] = kwargs["creds"]
|
|
28
|
+
|
|
29
|
+
if "timeout" not in self.params:
|
|
30
|
+
self.timeout: Optional[int] = None
|
|
31
|
+
|
|
32
|
+
self.endpoint: str = self.creds["endpoint"]
|
|
33
|
+
|
|
34
|
+
if self.creds["authtype"] == "basic":
|
|
35
|
+
self.username: str = self.creds["username"]
|
|
36
|
+
self.password: str = self.creds["password"]
|
|
37
|
+
self.r: requests.Session = requests.Session()
|
|
38
|
+
self.r.auth = (self.username, self.password)
|
|
39
|
+
self.r.headers.update({"Content-Type": "application/json; charset=UTF-8", "Accept": "application/json"})
|
|
40
|
+
|
|
41
|
+
if self.creds["authtype"] == "token":
|
|
42
|
+
self.token: str = self.creds["token"]
|
|
43
|
+
self.r = requests.Session()
|
|
44
|
+
self.r.headers.update({"Authorization": f"Token {self.token}"})
|
|
45
|
+
self.r.headers.update({"Content-Type": "application/json"})
|
|
46
|
+
|
|
47
|
+
def _request(
|
|
48
|
+
self, url: str, method: str, data: Optional[Any] = None, timeout: int = 30
|
|
49
|
+
) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
50
|
+
"""Execute the request."""
|
|
51
|
+
resp: requests.Response = self.r.request(method, self.endpoint + url, data=data, timeout=timeout)
|
|
52
|
+
|
|
53
|
+
# Some massaging to make our gerrit python code work
|
|
54
|
+
if resp.status_code == 409:
|
|
55
|
+
return resp
|
|
56
|
+
|
|
57
|
+
if resp.text:
|
|
58
|
+
try:
|
|
59
|
+
if "application/json" in resp.headers["Content-Type"]:
|
|
60
|
+
remove_xssi_magic: str = resp.text.replace(")]}'", "")
|
|
61
|
+
body: Optional[Dict[str, Any] | str] = json.loads(remove_xssi_magic)
|
|
62
|
+
else:
|
|
63
|
+
body = resp.text
|
|
64
|
+
except ValueError:
|
|
65
|
+
body = None
|
|
66
|
+
else:
|
|
67
|
+
body = None
|
|
68
|
+
return resp
|
|
69
|
+
|
|
70
|
+
return resp, body
|
|
71
|
+
|
|
72
|
+
def get(self, url: str, **kwargs) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
73
|
+
"""HTTP GET request."""
|
|
74
|
+
return self._request(url, "GET", **kwargs)
|
|
75
|
+
|
|
76
|
+
def patch(self, url: str, **kwargs) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
77
|
+
"""HTTP PATCH request."""
|
|
78
|
+
return self._request(url, "PATCH", **kwargs)
|
|
79
|
+
|
|
80
|
+
def post(self, url: str, **kwargs) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
81
|
+
"""HTTP POST request."""
|
|
82
|
+
return self._request(url, "POST", **kwargs)
|
|
83
|
+
|
|
84
|
+
def put(self, url: str, **kwargs) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
85
|
+
"""HTTP PUT request."""
|
|
86
|
+
return self._request(url, "PUT", **kwargs)
|
|
87
|
+
|
|
88
|
+
def delete(
|
|
89
|
+
self, url: str, **kwargs
|
|
90
|
+
) -> requests.Response | Tuple[requests.Response, Optional[Dict[str, Any] | str]]:
|
|
91
|
+
"""HTTP DELETE request."""
|
|
92
|
+
return self._request(url, "DELETE", **kwargs)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# SPDX-License-Identifier: EPL-1.0
|
|
2
|
+
##############################################################################
|
|
3
|
+
# Copyright (c) 2019 The Linux Foundation and others.
|
|
4
|
+
#
|
|
5
|
+
# All rights reserved. This program and the accompanying materials
|
|
6
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
7
|
+
# which accompanies this distribution, and is available at
|
|
8
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
9
|
+
##############################################################################
|
|
10
|
+
"""Init for endpoints module."""
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# SPDX-License-Identifier: EPL-1.0
|
|
2
|
+
##############################################################################
|
|
3
|
+
# Copyright (c) 2019 The Linux Foundation and others.
|
|
4
|
+
#
|
|
5
|
+
# All rights reserved. This program and the accompanying materials
|
|
6
|
+
# are made available under the terms of the Eclipse Public License v1.0
|
|
7
|
+
# which accompanies this distribution, and is available at
|
|
8
|
+
# http://www.eclipse.org/legal/epl-v10.html
|
|
9
|
+
##############################################################################
|
|
10
|
+
|
|
11
|
+
"""Gerrit REST API interface."""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import time
|
|
17
|
+
import urllib
|
|
18
|
+
|
|
19
|
+
import lftools_uv.api.client as client
|
|
20
|
+
from lftools_uv import config
|
|
21
|
+
|
|
22
|
+
log = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Gerrit(client.RestApi):
|
|
26
|
+
"""API endpoint wrapper for Gerrit.
|
|
27
|
+
|
|
28
|
+
Be sure to always include the trailing "/" when adding
|
|
29
|
+
new methods.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, **params):
|
|
33
|
+
"""Initialize the class."""
|
|
34
|
+
self.params = params
|
|
35
|
+
self.fqdn = self.params["fqdn"]
|
|
36
|
+
if "creds" not in self.params:
|
|
37
|
+
creds = {
|
|
38
|
+
"authtype": "basic",
|
|
39
|
+
"username": config.get_setting(self.fqdn, "username"),
|
|
40
|
+
"password": config.get_setting(self.fqdn, "password"),
|
|
41
|
+
"endpoint": config.get_setting(self.fqdn, "endpoint"),
|
|
42
|
+
}
|
|
43
|
+
params["creds"] = creds
|
|
44
|
+
|
|
45
|
+
super(Gerrit, self).__init__(**params)
|
|
46
|
+
|
|
47
|
+
def add_file(self, fqdn, gerrit_project, filename, issue_id, file_location, **kwargs):
|
|
48
|
+
"""Add a file for review to a Project.
|
|
49
|
+
|
|
50
|
+
File can be sourced from any location
|
|
51
|
+
but only lands in the root of the repo.
|
|
52
|
+
unless file_location is specified
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
gerrit_url gerrit.o-ran-sc.org
|
|
56
|
+
gerrit_project test/test1
|
|
57
|
+
filename /tmp/INFO.yaml
|
|
58
|
+
file_location="somedir/example-INFO.yaml"
|
|
59
|
+
"""
|
|
60
|
+
signed_off_by = config.get_setting(fqdn, "sob")
|
|
61
|
+
basename = os.path.basename(filename)
|
|
62
|
+
payload = self.create_change(basename, gerrit_project, issue_id, signed_off_by)
|
|
63
|
+
|
|
64
|
+
if file_location:
|
|
65
|
+
file_location = urllib.parse.quote(file_location, safe="", encoding=None, errors=None)
|
|
66
|
+
basename = file_location
|
|
67
|
+
log.info(payload)
|
|
68
|
+
|
|
69
|
+
access_str = "changes/"
|
|
70
|
+
result = self.post(access_str, data=payload)[1]
|
|
71
|
+
log.info(result["id"])
|
|
72
|
+
changeid = result["id"]
|
|
73
|
+
|
|
74
|
+
my_file = open(filename)
|
|
75
|
+
my_file_size = os.stat(filename)
|
|
76
|
+
headers = {"Content-Type": "text/plain", "Content-length": "{}".format(my_file_size)}
|
|
77
|
+
self.r.headers.update(headers)
|
|
78
|
+
access_str = "changes/{}/edit/{}".format(changeid, basename)
|
|
79
|
+
payload = my_file
|
|
80
|
+
result = self.put(access_str, data=payload)
|
|
81
|
+
log.info(result)
|
|
82
|
+
|
|
83
|
+
access_str = "changes/{}/edit:publish".format(changeid)
|
|
84
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
85
|
+
self.r.headers.update(headers)
|
|
86
|
+
payload = json.dumps(
|
|
87
|
+
{
|
|
88
|
+
"notify": "NONE",
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
result = self.post(access_str, data=payload)
|
|
92
|
+
return result
|
|
93
|
+
##############################################################
|
|
94
|
+
|
|
95
|
+
def add_info_job(self, fqdn, gerrit_project, jjbrepo, reviewid, issue_id, **kwargs):
|
|
96
|
+
"""Add an INFO job for a new Project.
|
|
97
|
+
|
|
98
|
+
Adds info verify jenkins job for project.
|
|
99
|
+
result['id'] can be used to amend a review
|
|
100
|
+
so that multiple projects can have info jobs added
|
|
101
|
+
in a single review
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
|
|
105
|
+
fqdn gerrit.o-ran-sc.org
|
|
106
|
+
gerrit_project test/test1
|
|
107
|
+
jjbrepo ci-mangement
|
|
108
|
+
"""
|
|
109
|
+
###############################################################
|
|
110
|
+
# Setup
|
|
111
|
+
signed_off_by = config.get_setting(fqdn, "sob")
|
|
112
|
+
gerrit_project_dashed = gerrit_project.replace("/", "-")
|
|
113
|
+
filename = "{}.yaml".format(gerrit_project_dashed)
|
|
114
|
+
|
|
115
|
+
if not reviewid:
|
|
116
|
+
payload = self.create_change(filename, jjbrepo, issue_id, signed_off_by)
|
|
117
|
+
log.info(payload)
|
|
118
|
+
access_str = "changes/"
|
|
119
|
+
result = self.post(access_str, data=payload)[1]
|
|
120
|
+
log.info(result)
|
|
121
|
+
log.info(result["id"])
|
|
122
|
+
changeid = result["id"]
|
|
123
|
+
else:
|
|
124
|
+
changeid = reviewid
|
|
125
|
+
|
|
126
|
+
if fqdn == "gerrit.o-ran-sc.org":
|
|
127
|
+
buildnode = "centos7-builder-1c-1g"
|
|
128
|
+
elif fqdn == "gerrit.onap.org":
|
|
129
|
+
buildnode = "centos8-builder-2c-1g"
|
|
130
|
+
else:
|
|
131
|
+
buildnode = "centos7-builder-2c-1g"
|
|
132
|
+
|
|
133
|
+
my_inline_file = """---
|
|
134
|
+
- project:
|
|
135
|
+
name: {0}-project-view
|
|
136
|
+
project-name: {0}
|
|
137
|
+
views:
|
|
138
|
+
- project-view\n
|
|
139
|
+
- project:
|
|
140
|
+
name: {0}-info
|
|
141
|
+
project: {1}
|
|
142
|
+
project-name: {0}
|
|
143
|
+
build-node: {2}
|
|
144
|
+
jobs:
|
|
145
|
+
- gerrit-info-yaml-verify\n""".format(
|
|
146
|
+
gerrit_project_dashed, gerrit_project, buildnode
|
|
147
|
+
)
|
|
148
|
+
my_inline_file_size = len(my_inline_file.encode("utf-8"))
|
|
149
|
+
headers = {"Content-Type": "text/plain", "Content-length": "{}".format(my_inline_file_size)}
|
|
150
|
+
self.r.headers.update(headers)
|
|
151
|
+
access_str = "changes/{0}/edit/jjb%2F{1}%2F{1}.yaml".format(changeid, gerrit_project_dashed)
|
|
152
|
+
payload = my_inline_file
|
|
153
|
+
log.info(access_str)
|
|
154
|
+
result = self.put(access_str, data=payload)
|
|
155
|
+
log.info(result)
|
|
156
|
+
|
|
157
|
+
access_str = "changes/{}/edit:publish".format(changeid)
|
|
158
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
159
|
+
self.r.headers.update(headers)
|
|
160
|
+
payload = json.dumps(
|
|
161
|
+
{
|
|
162
|
+
"notify": "NONE",
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
result = self.post(access_str, data=payload)
|
|
166
|
+
log.info(result)
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def vote_on_change(self, fqdn, gerrit_project, changeid, **kwargs):
|
|
170
|
+
"""Helper that votes on a change.
|
|
171
|
+
|
|
172
|
+
POST /changes/{change-id}/revisions/{revision-id}/review
|
|
173
|
+
"""
|
|
174
|
+
log.info(fqdn, gerrit_project, changeid)
|
|
175
|
+
access_str = "changes/{}/revisions/2/review".format(changeid)
|
|
176
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
177
|
+
self.r.headers.update(headers)
|
|
178
|
+
payload = json.dumps(
|
|
179
|
+
{
|
|
180
|
+
"tag": "automation",
|
|
181
|
+
"message": "Vote on file",
|
|
182
|
+
"labels": {
|
|
183
|
+
"Verified": +1,
|
|
184
|
+
"Code-Review": +2,
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
result = self.post(access_str, data=payload)
|
|
190
|
+
# Code for projects that don't allow self merge.
|
|
191
|
+
if config.get_setting(self.fqdn + ".second"):
|
|
192
|
+
second_username = config.get_setting(self.fqdn + ".second", "username")
|
|
193
|
+
second_password = config.get_setting(self.fqdn + ".second", "password")
|
|
194
|
+
self.r.auth = (second_username, second_password)
|
|
195
|
+
result = self.post(access_str, data=payload)
|
|
196
|
+
self.r.auth = (self.username, self.password)
|
|
197
|
+
return result
|
|
198
|
+
|
|
199
|
+
def submit_change(self, fqdn, gerrit_project, changeid, payload, **kwargs):
|
|
200
|
+
"""Method so submit a change."""
|
|
201
|
+
# submit a change id
|
|
202
|
+
access_str = "changes/{}/submit".format(changeid)
|
|
203
|
+
log.info(access_str)
|
|
204
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
205
|
+
self.r.headers.update(headers)
|
|
206
|
+
result = self.post(access_str, data=payload)
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
def abandon_changes(self, fqdn, gerrit_project, **kwargs):
|
|
210
|
+
"""."""
|
|
211
|
+
gerrit_project_encoded = urllib.parse.quote(gerrit_project, safe="", encoding=None, errors=None)
|
|
212
|
+
access_str = "changes/?q=project:{}".format(gerrit_project_encoded)
|
|
213
|
+
log.info(access_str)
|
|
214
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
215
|
+
self.r.headers.update(headers)
|
|
216
|
+
result = self.get(access_str)[1]
|
|
217
|
+
payload = {"message": "Abandoned by automation"}
|
|
218
|
+
for id in result:
|
|
219
|
+
if (id["status"]) == "NEW":
|
|
220
|
+
id = id["id"]
|
|
221
|
+
access_str = "changes/{}/abandon".format(id)
|
|
222
|
+
log.info(access_str)
|
|
223
|
+
result = self.post(access_str, data=payload)[1]
|
|
224
|
+
return result
|
|
225
|
+
|
|
226
|
+
def create_change(self, filename, gerrit_project, issue_id, signed_off_by, **kwargs):
|
|
227
|
+
"""Method to create a gerrit change."""
|
|
228
|
+
if issue_id:
|
|
229
|
+
subject = "Automation adds {0}\n\nIssue-ID: {1}\n\nSigned-off-by: {2}".format(
|
|
230
|
+
filename, issue_id, signed_off_by
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
subject = "Automation adds {0}\n\nSigned-off-by: {1}".format(filename, signed_off_by)
|
|
234
|
+
payload = json.dumps(
|
|
235
|
+
{
|
|
236
|
+
"project": "{}".format(gerrit_project),
|
|
237
|
+
"subject": "{}".format(subject),
|
|
238
|
+
"branch": "master",
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
return payload
|
|
242
|
+
|
|
243
|
+
def sanity_check(self, fqdn, gerrit_project, **kwargs):
|
|
244
|
+
"""Perform a sanity check."""
|
|
245
|
+
# Sanity check
|
|
246
|
+
gerrit_project_encoded = urllib.parse.quote(gerrit_project, safe="", encoding=None, errors=None)
|
|
247
|
+
mylist = ["projects/", "projects/{}".format(gerrit_project_encoded)]
|
|
248
|
+
for access_str in mylist:
|
|
249
|
+
log.info(access_str)
|
|
250
|
+
try:
|
|
251
|
+
result = self.get(access_str)[1]
|
|
252
|
+
except Exception:
|
|
253
|
+
log.info("Not found {}".format(access_str))
|
|
254
|
+
exit(1)
|
|
255
|
+
log.info("found {} {}".format(access_str, mylist))
|
|
256
|
+
return result
|
|
257
|
+
|
|
258
|
+
def add_git_review(self, fqdn, gerrit_project, issue_id, **kwargs):
|
|
259
|
+
"""Add and Submit a .gitreview for a project.
|
|
260
|
+
|
|
261
|
+
Example:
|
|
262
|
+
|
|
263
|
+
fqdn gerrit.o-ran-sc.org
|
|
264
|
+
gerrit_project test/test1
|
|
265
|
+
issue_id: CIMAN-33
|
|
266
|
+
"""
|
|
267
|
+
signed_off_by = config.get_setting(fqdn, "sob")
|
|
268
|
+
self.sanity_check(fqdn, gerrit_project)
|
|
269
|
+
|
|
270
|
+
###############################################################
|
|
271
|
+
# Create A change set.
|
|
272
|
+
filename = ".gitreview"
|
|
273
|
+
payload = self.create_change(filename, gerrit_project, issue_id, signed_off_by)
|
|
274
|
+
log.info(payload)
|
|
275
|
+
|
|
276
|
+
access_str = "changes/"
|
|
277
|
+
result = self.post(access_str, data=payload)[1]
|
|
278
|
+
log.info(result)
|
|
279
|
+
changeid = result["id"]
|
|
280
|
+
|
|
281
|
+
###############################################################
|
|
282
|
+
# Add a file to a change set.
|
|
283
|
+
my_inline_file = """
|
|
284
|
+
[gerrit]
|
|
285
|
+
host={0}
|
|
286
|
+
port=29418
|
|
287
|
+
project={1}
|
|
288
|
+
defaultbranch=master
|
|
289
|
+
""".format(
|
|
290
|
+
fqdn, gerrit_project
|
|
291
|
+
)
|
|
292
|
+
my_inline_file_size = len(my_inline_file.encode("utf-8"))
|
|
293
|
+
headers = {"Content-Type": "text/plain", "Content-length": "{}".format(my_inline_file_size)}
|
|
294
|
+
self.r.headers.update(headers)
|
|
295
|
+
access_str = "changes/{}/edit/{}".format(changeid, filename)
|
|
296
|
+
payload = my_inline_file
|
|
297
|
+
result = self.put(access_str, data=payload)
|
|
298
|
+
|
|
299
|
+
if result.status_code == 409:
|
|
300
|
+
log.info(result)
|
|
301
|
+
log.info("Conflict detected exiting")
|
|
302
|
+
exit(0)
|
|
303
|
+
|
|
304
|
+
else:
|
|
305
|
+
access_str = "changes/{}/edit:publish".format(changeid)
|
|
306
|
+
headers = {"Content-Type": "application/json; charset=UTF-8"}
|
|
307
|
+
self.r.headers.update(headers)
|
|
308
|
+
payload = json.dumps(
|
|
309
|
+
{
|
|
310
|
+
"notify": "NONE",
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
result = self.post(access_str, data=payload)
|
|
314
|
+
log.info(result)
|
|
315
|
+
|
|
316
|
+
result = self.vote_on_change(fqdn, gerrit_project, changeid)
|
|
317
|
+
log.info(result)
|
|
318
|
+
|
|
319
|
+
time.sleep(5)
|
|
320
|
+
result = self.submit_change(fqdn, gerrit_project, changeid, payload)
|
|
321
|
+
log.info(result)
|
|
322
|
+
|
|
323
|
+
def create_saml_group(self, fqdn, ldap_group, **kwargs):
|
|
324
|
+
"""Create saml group from ldap group."""
|
|
325
|
+
###############################################################
|
|
326
|
+
payload = json.dumps({"visible_to_all": "false"})
|
|
327
|
+
saml_group = "saml/{}".format(ldap_group)
|
|
328
|
+
saml_group_encoded = urllib.parse.quote(saml_group, safe="", encoding=None, errors=None)
|
|
329
|
+
access_str = "groups/{}".format(saml_group_encoded)
|
|
330
|
+
log.info("Encoded SAML group name: {}".format(saml_group_encoded))
|
|
331
|
+
result = self.put(access_str, data=payload)
|
|
332
|
+
return result
|
|
333
|
+
|
|
334
|
+
def add_github_rights(self, fqdn, gerrit_project, **kwargs):
|
|
335
|
+
"""Grant github read to a project."""
|
|
336
|
+
###############################################################
|
|
337
|
+
# Github Rights
|
|
338
|
+
|
|
339
|
+
gerrit_project_encoded = urllib.parse.quote(gerrit_project, safe="", encoding=None, errors=None)
|
|
340
|
+
# GET /groups/?m=test%2F HTTP/1.0
|
|
341
|
+
access_str = "groups/?m=GitHub%20Replication"
|
|
342
|
+
log.info(access_str)
|
|
343
|
+
result = self.get(access_str)[1]
|
|
344
|
+
time.sleep(5)
|
|
345
|
+
githubid = result["GitHub Replication"]["id"]
|
|
346
|
+
log.info(githubid)
|
|
347
|
+
|
|
348
|
+
# POST /projects/MyProject/access HTTP/1.0
|
|
349
|
+
if githubid:
|
|
350
|
+
payload = json.dumps(
|
|
351
|
+
{
|
|
352
|
+
"add": {
|
|
353
|
+
"refs/*": {
|
|
354
|
+
"permissions": {
|
|
355
|
+
"read": {"rules": {"{}".format(githubid): {"action": "{}".format("ALLOW")}}}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
access_str = "projects/{}/access".format(gerrit_project_encoded)
|
|
362
|
+
result = self.post(access_str, data=payload)[1]
|
|
363
|
+
pretty = json.dumps(result, indent=4, sort_keys=True)
|
|
364
|
+
log.info(pretty)
|
|
365
|
+
else:
|
|
366
|
+
log.info("Error no githubid found")
|
|
367
|
+
|
|
368
|
+
def create_project(self, fqdn, gerrit_project, ldap_group, description, check):
|
|
369
|
+
"""Create a project via the gerrit API.
|
|
370
|
+
|
|
371
|
+
Creates a gerrit project.
|
|
372
|
+
Converts ldap group to saml group and sets as owner.
|
|
373
|
+
|
|
374
|
+
Example:
|
|
375
|
+
|
|
376
|
+
gerrit_url gerrit.o-ran-sc.org/r
|
|
377
|
+
gerrit_project test/test1
|
|
378
|
+
ldap_group oran-gerrit-test-test1-committers
|
|
379
|
+
--description="This is a demo project"
|
|
380
|
+
|
|
381
|
+
"""
|
|
382
|
+
gerrit_project = urllib.parse.quote(gerrit_project, safe="", encoding=None, errors=None)
|
|
383
|
+
|
|
384
|
+
access_str = "projects/?query=name:{}".format(gerrit_project)
|
|
385
|
+
result = self.get(access_str)[0]
|
|
386
|
+
jsonText = result.text.replace(")]}'\n", "").strip()
|
|
387
|
+
|
|
388
|
+
try:
|
|
389
|
+
resultsDict = json.loads(jsonText)
|
|
390
|
+
except json.decoder.JSONDecodeError:
|
|
391
|
+
log.info(result)
|
|
392
|
+
log.info("A problem was encountered while querying the Gerrit API.")
|
|
393
|
+
log.debug(result.text)
|
|
394
|
+
exit(result.status_code)
|
|
395
|
+
|
|
396
|
+
if resultsDict:
|
|
397
|
+
log.info("Project already exists")
|
|
398
|
+
exit(1)
|
|
399
|
+
if check:
|
|
400
|
+
exit(0)
|
|
401
|
+
|
|
402
|
+
saml_group = "saml/{}".format(ldap_group)
|
|
403
|
+
log.info("SAML group name: {}".format(saml_group))
|
|
404
|
+
|
|
405
|
+
access_str = "projects/{}".format(gerrit_project)
|
|
406
|
+
payload = json.dumps(
|
|
407
|
+
{
|
|
408
|
+
"description": "{}".format(description),
|
|
409
|
+
"submit_type": "INHERIT",
|
|
410
|
+
"create_empty_commit": "True",
|
|
411
|
+
"owners": ["{}".format(saml_group)],
|
|
412
|
+
}
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
log.info(payload)
|
|
416
|
+
result = self.put(access_str, data=payload)
|
|
417
|
+
return result
|
|
418
|
+
|
|
419
|
+
def list_project_permissions(self, project):
|
|
420
|
+
"""List a projects owners."""
|
|
421
|
+
result = self.get("access/?project={}".format(project))[1][project]["local"]
|
|
422
|
+
group_list = []
|
|
423
|
+
for k, v in result.items():
|
|
424
|
+
for kk, vv in result[k]["permissions"]["owner"]["rules"].items():
|
|
425
|
+
group_list.append(kk.replace("ldap:cn=", "").replace(",ou=Groups,dc=freestandards,dc=org", ""))
|
|
426
|
+
return group_list
|
|
427
|
+
|
|
428
|
+
def list_project_inherits_from(self, gerrit_project):
|
|
429
|
+
"""List who a project inherits from."""
|
|
430
|
+
gerrit_project = urllib.parse.quote(gerrit_project, safe="", encoding=None, errors=None)
|
|
431
|
+
result = self.get("projects/{}/access".format(gerrit_project))[1]
|
|
432
|
+
inherits = result["inherits_from"]["id"]
|
|
433
|
+
return inherits
|