diff options
Diffstat (limited to '')
-rw-r--r-- | src/pybind/mgr/feedback/__init__.py | 2 | ||||
-rw-r--r-- | src/pybind/mgr/feedback/model.py | 47 | ||||
-rw-r--r-- | src/pybind/mgr/feedback/module.py | 139 | ||||
-rw-r--r-- | src/pybind/mgr/feedback/service.py | 49 |
4 files changed, 237 insertions, 0 deletions
diff --git a/src/pybind/mgr/feedback/__init__.py b/src/pybind/mgr/feedback/__init__.py new file mode 100644 index 000000000..0bc7059e7 --- /dev/null +++ b/src/pybind/mgr/feedback/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from .module import FeedbackModule
\ No newline at end of file diff --git a/src/pybind/mgr/feedback/model.py b/src/pybind/mgr/feedback/model.py new file mode 100644 index 000000000..902f18256 --- /dev/null +++ b/src/pybind/mgr/feedback/model.py @@ -0,0 +1,47 @@ +# # -*- coding: utf-8 -*- +from enum import Enum + + +class Feedback: + project_id: int + tracker_id: int + subject: str + description: str + status: int + + class Project(Enum): + dashboard = 46 + block = 9 # rbd + object = 10 # rgw + file_system = 13 # cephfs + ceph_manager = 46 + orchestrator = 42 + ceph_volume = 39 + core_ceph = 36 # rados + + class TrackerType(Enum): + bug = 1 + feature = 2 + + class Status(Enum): + new = 1 + + def __init__(self, project_id, tracker_id, subject, description): + self.project_id = int(project_id) + self.tracker_id = int(tracker_id) + self.subject = subject + self.description = description + self.status = Feedback.Status.new.value + + def as_dict(self): + return { + "issue": { + "project": { + "id": self.project_id + }, + "tracker_id": self.tracker_id, + "Status": self.status, + "subject": self.subject, + "description": self.description + } + } diff --git a/src/pybind/mgr/feedback/module.py b/src/pybind/mgr/feedback/module.py new file mode 100644 index 000000000..95683912c --- /dev/null +++ b/src/pybind/mgr/feedback/module.py @@ -0,0 +1,139 @@ + +""" +Feedback module + +See doc/mgr/feedback.rst for more info. +""" + +from requests.exceptions import RequestException + +from mgr_module import CLIReadCommand, HandleCommandResult, MgrModule +import errno + +from .service import CephTrackerClient +from .model import Feedback + + +class FeedbackModule(MgrModule): + + # there are CLI commands we implement + @CLIReadCommand('feedback set api-key') + def _cmd_feedback_set_api_key(self, key: str) -> HandleCommandResult: + """ + Set Ceph Issue Tracker API key + """ + try: + self.set_store('api_key', key) + except Exception as error: + return HandleCommandResult(stderr=f'Exception in setting API key : {error}') + return HandleCommandResult(stdout="Successfully updated API key") + + @CLIReadCommand('feedback delete api-key') + def _cmd_feedback_delete_api_key(self) -> HandleCommandResult: + """ + Delete Ceph Issue Tracker API key + """ + try: + self.set_store('api_key', None) + except Exception as error: + return HandleCommandResult(stderr=f'Exception in deleting API key : {error}') + return HandleCommandResult(stdout="Successfully deleted key") + + @CLIReadCommand('feedback get api-key') + def _cmd_feedback_get_api_key(self) -> HandleCommandResult: + """ + Get Ceph Issue Tracker API key + """ + try: + key = self.get_store('api_key') + if key is None: + return HandleCommandResult(stderr='Issue tracker key is not set. Set key with `ceph feedback api-key set <your_key>`') + except Exception as error: + return HandleCommandResult(stderr=f'Error in retreiving issue tracker API key: {error}') + return HandleCommandResult(stdout=f'Your key: {key}') + + @CLIReadCommand('feedback issue list') + def _cmd_feedback_issue_list(self) -> HandleCommandResult: + """ + Fetch issue list + """ + tracker_client = CephTrackerClient() + try: + response = tracker_client.list_issues() + except Exception: + return HandleCommandResult(stderr="Error occurred. Try again later") + return HandleCommandResult(stdout=str(response)) + + @CLIReadCommand('feedback issue report') + def _cmd_feedback_issue_report(self, project: str, tracker: str, subject: str, description: str) -> HandleCommandResult: + """ + Create an issue + """ + try: + feedback = Feedback(Feedback.Project[project].value, + Feedback.TrackerType[tracker].value, subject, description) + except KeyError: + return -errno.EINVAL, '', 'Invalid arguments' + try: + current_api_key = self.get_store('api_key') + if current_api_key is None: + return HandleCommandResult(stderr='Issue tracker key is not set. Set key with `ceph set issue_key <your_key>`') + except Exception as error: + return HandleCommandResult(stderr=f'Error in retreiving issue tracker API key: {error}') + tracker_client = CephTrackerClient() + try: + response = tracker_client.create_issue(feedback, current_api_key) + except RequestException as error: + return HandleCommandResult(stderr=f'Error in creating issue: {str(error)}. Please set valid API key.') + return HandleCommandResult(stdout=f'{str(response)}') + + def set_api_key(self, key: str): + try: + self.set_store('api_key', key) + except Exception as error: + raise RequestException(f'Exception in setting API key : {error}') + return 'Successfully updated API key' + + def get_api_key(self): + try: + key = self.get_store('api_key') + except Exception as error: + raise RequestException(f'Error in retreiving issue tracker API key : {error}') + return key + + def is_api_key_set(self): + try: + key = self.get_store('api_key') + except Exception as error: + raise RequestException(f'Error in retreiving issue tracker API key : {error}') + if key is None: + return False + return key != '' + + def delete_api_key(self): + try: + self.set_store('api_key', None) + except Exception as error: + raise RequestException(f'Exception in deleting API key : {error}') + return 'Successfully deleted API key' + + def get_issues(self): + tracker_client = CephTrackerClient() + return tracker_client.list_issues() + + def validate_and_create_issue(self, project: str, tracker: str, subject: str, description: str, api_key=None): + feedback = Feedback(Feedback.Project[project].value, + Feedback.TrackerType[tracker].value, subject, description) + tracker_client = CephTrackerClient() + stored_api_key = self.get_store('api_key') + try: + if api_key: + result = tracker_client.create_issue(feedback, api_key) + else: + result = tracker_client.create_issue(feedback, stored_api_key) + except RequestException: + self.set_store('api_key', None) + raise + if not stored_api_key: + self.set_store('api_key', api_key) + return result diff --git a/src/pybind/mgr/feedback/service.py b/src/pybind/mgr/feedback/service.py new file mode 100644 index 000000000..dc8c6b64a --- /dev/null +++ b/src/pybind/mgr/feedback/service.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import json +import requests +from requests.exceptions import RequestException + +from .model import Feedback + +class config: + url = 'tracker.ceph.com' + port = 443 + +class CephTrackerClient(): + + def list_issues(self): + ''' + Fetch an issue from the Ceph Issue tracker + ''' + headers = { + 'Content-Type': 'application/json', + } + response = requests.get( + f'https://{config.url}/issues.json', headers=headers) + if not response.ok: + if response.status_code == 404: + raise FileNotFoundError + raise RequestException(response.status_code) + return {"message": response.json()} + + def create_issue(self, feedback: Feedback, api_key: str): + ''' + Create an issue in the Ceph Issue tracker + ''' + try: + headers = { + 'Content-Type': 'application/json', + 'X-Redmine-API-Key': api_key, + } + except KeyError: + raise Exception("Ceph Tracker API Key not set") + data = json.dumps(feedback.as_dict()) + response = requests.post( + f'https://{config.url}/projects/{feedback.project_id}/issues.json', + headers=headers, data=data) + if not response.ok: + if response.status_code == 401: + raise RequestException("Unauthorized. Invalid issue tracker API key") + raise RequestException(response.reason) + return {"message": response.json()} |