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.
Files changed (98) hide show
  1. lftools_uv/__init__.py +44 -0
  2. lftools_uv/api/__init__.py +10 -0
  3. lftools_uv/api/client.py +92 -0
  4. lftools_uv/api/endpoints/__init__.py +10 -0
  5. lftools_uv/api/endpoints/gerrit.py +433 -0
  6. lftools_uv/api/endpoints/nexus2.py +301 -0
  7. lftools_uv/api/endpoints/nexus3.py +408 -0
  8. lftools_uv/api/endpoints/readthedocs.py +269 -0
  9. lftools_uv/api/exceptions.py +19 -0
  10. lftools_uv/cli/__init__.py +125 -0
  11. lftools_uv/cli/config.py +63 -0
  12. lftools_uv/cli/dco.py +89 -0
  13. lftools_uv/cli/deploy.py +329 -0
  14. lftools_uv/cli/gerrit.py +233 -0
  15. lftools_uv/cli/github_cli.py +282 -0
  16. lftools_uv/cli/infofile.py +354 -0
  17. lftools_uv/cli/jenkins/__init__.py +244 -0
  18. lftools_uv/cli/jenkins/builds.py +53 -0
  19. lftools_uv/cli/jenkins/jobs.py +68 -0
  20. lftools_uv/cli/jenkins/nodes.py +42 -0
  21. lftools_uv/cli/jenkins/plugins.py +188 -0
  22. lftools_uv/cli/jenkins/token.py +176 -0
  23. lftools_uv/cli/ldap_cli.py +187 -0
  24. lftools_uv/cli/lfidapi.py +86 -0
  25. lftools_uv/cli/license.py +64 -0
  26. lftools_uv/cli/nexus.py +236 -0
  27. lftools_uv/cli/nexus2/__init__.py +39 -0
  28. lftools_uv/cli/nexus2/privilege.py +58 -0
  29. lftools_uv/cli/nexus2/repository.py +61 -0
  30. lftools_uv/cli/nexus2/role.py +60 -0
  31. lftools_uv/cli/nexus2/user.py +61 -0
  32. lftools_uv/cli/nexus3/__init__.py +46 -0
  33. lftools_uv/cli/nexus3/asset.py +55 -0
  34. lftools_uv/cli/nexus3/privilege.py +36 -0
  35. lftools_uv/cli/nexus3/repository.py +36 -0
  36. lftools_uv/cli/nexus3/role.py +50 -0
  37. lftools_uv/cli/nexus3/script.py +87 -0
  38. lftools_uv/cli/nexus3/tag.py +67 -0
  39. lftools_uv/cli/nexus3/task.py +41 -0
  40. lftools_uv/cli/nexus3/user.py +74 -0
  41. lftools_uv/cli/no_cmd.py +26 -0
  42. lftools_uv/cli/rtd.py +223 -0
  43. lftools_uv/cli/schema.py +40 -0
  44. lftools_uv/cli/sign.py +135 -0
  45. lftools_uv/cli/utils.py +43 -0
  46. lftools_uv/cli/version.py +110 -0
  47. lftools_uv/config.py +64 -0
  48. lftools_uv/conftest.py +9 -0
  49. lftools_uv/deploy.py +945 -0
  50. lftools_uv/git/__init__.py +10 -0
  51. lftools_uv/git/gerrit.py +280 -0
  52. lftools_uv/git/templates/config-params.yaml +3 -0
  53. lftools_uv/git/templates/gitreview +5 -0
  54. lftools_uv/git/templates/project.yaml +15 -0
  55. lftools_uv/git/templates/serverCredentialMappings.yaml +18 -0
  56. lftools_uv/github_helper.py +230 -0
  57. lftools_uv/helpers.py +21 -0
  58. lftools_uv/jenkins/__init__.py +73 -0
  59. lftools_uv/jenkins/token.py +52 -0
  60. lftools_uv/ldap_cli.py +21 -0
  61. lftools_uv/lfidapi.py +212 -0
  62. lftools_uv/license.py +81 -0
  63. lftools_uv/nexus/__init__.py +332 -0
  64. lftools_uv/nexus/cmd.py +544 -0
  65. lftools_uv/nexus/release_docker_hub.py +919 -0
  66. lftools_uv/nexus/util.py +34 -0
  67. lftools_uv/oauth2_helper.py +42 -0
  68. lftools_uv/openstack/__init__.py +12 -0
  69. lftools_uv/openstack/cmd.py +256 -0
  70. lftools_uv/openstack/image.py +241 -0
  71. lftools_uv/openstack/no_cmd.py +26 -0
  72. lftools_uv/openstack/object.py +25 -0
  73. lftools_uv/openstack/server.py +95 -0
  74. lftools_uv/openstack/stack.py +210 -0
  75. lftools_uv/openstack/volume.py +98 -0
  76. lftools_uv/schema.py +46 -0
  77. lftools_uv/shell/__init__.py +11 -0
  78. lftools_uv/shell/autocorrectinfofile.py +42 -0
  79. lftools_uv/shell/dco.py +144 -0
  80. lftools_uv/shell/deploy.py +42 -0
  81. lftools_uv/shell/fix_yamllint.py +42 -0
  82. lftools_uv/shell/gerrit_create.py +42 -0
  83. lftools_uv/shell/inactivecommitters.py +42 -0
  84. lftools_uv/shell/sign.py +42 -0
  85. lftools_uv/shell/version.py +42 -0
  86. lftools_uv/shell/yaml4info.py +42 -0
  87. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/autocorrectinfofile +169 -0
  88. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/deploy +850 -0
  89. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/fix_yamllint +129 -0
  90. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/gerrit_create +253 -0
  91. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/inactivecommitters +108 -0
  92. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/sign +331 -0
  93. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/version +176 -0
  94. lftools_uv-0.1.0.data/data/share/lftools-uv/shell/yaml4info +64 -0
  95. lftools_uv-0.1.0.dist-info/METADATA +271 -0
  96. lftools_uv-0.1.0.dist-info/RECORD +98 -0
  97. lftools_uv-0.1.0.dist-info/WHEEL +4 -0
  98. 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."""
@@ -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