summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/feedback
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/pybind/mgr/feedback/__init__.py2
-rw-r--r--src/pybind/mgr/feedback/model.py47
-rw-r--r--src/pybind/mgr/feedback/module.py139
-rw-r--r--src/pybind/mgr/feedback/service.py49
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()}