Merge ~mateuszsusik/dpcs:aplikacjaklienta into dpcs:master

Proposed by Mateusz Susik
Status: Merged
Merge reported by: Marek Bardoński
Merged at revision: not available
Proposed branch: ~mateuszsusik/dpcs:aplikacjaklienta
Merge into: dpcs:master
Diff against target: 437 lines (+363/-5)
9 files modified
.gitignore (+81/-0)
__init__.py (+0/-0)
client/__init__.py (+0/-0)
client/main/__init__.py (+3/-0)
client/main/client.py (+164/-0)
client/main/demon.py (+91/-0)
client/main/mock.py (+13/-0)
systemcheck/__init__.py (+0/-0)
systemcheck/systemcheck.py (+11/-5)
Reviewer Review Type Date Requested Status
Paweł Goliński Pending
Marek Bardoński Pending
Review via email: mp+288357@code.launchpad.net

Description of the change

Run as `python2 -m dpcs.client.main.client 'polecenie'`

When the installation ticket is resolved, the above will be obsolete.

Additionally an example of a daemon, it will be useful in future, and a .gitignore.

To post a comment you must log in.
Revision history for this message
Mateusz Susik (mateuszsusik) wrote :

Adding Paweł Goliński as a reviewer, as I modified his code.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.gitignore b/.gitignore
0new file mode 1006440new file mode 100644
index 0000000..908a064
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,81 @@
1# Byte-compiled / optimized / DLL files
2__pycache__/
3*.py[cod]
4*$py.class
5
6# C extensions
7*.so
8
9# Distribution / packaging
10.Python
11env/
12build/
13develop-eggs/
14dist/
15downloads/
16eggs/
17.eggs/
18lib/
19lib64/
20parts/
21sdist/
22var/
23*.egg-info/
24.installed.cfg
25*.egg
26
27# PyInstaller
28# Usually these files are written by a python script from a template
29# before PyInstaller builds the exe, so as to inject date/other infos into it.
30*.manifest
31*.spec
32
33# Installer logs
34pip-log.txt
35pip-delete-this-directory.txt
36
37# Unit test / coverage reports
38htmlcov/
39.tox/
40.coverage
41.coverage.*
42.cache
43nosetests.xml
44coverage.xml
45*,cover
46.hypothesis/
47
48# Translations
49*.mo
50*.pot
51
52# Django stuff:
53*.log
54local_settings.py
55
56# Flask instance folder
57instance/
58
59# Scrapy stuff:
60.scrapy
61
62# Sphinx documentation
63docs/_build/
64
65# PyBuilder
66target/
67
68# IPython Notebook
69.ipynb_checkpoints
70
71# pyenv
72.python-version
73
74# celery beat schedule file
75celerybeat-schedule
76
77# dotenv
78.env
79
80# Spyder project settings
81.spyderproject
diff --git a/__init__.py b/__init__.py
0new file mode 10064482new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/__init__.py
diff --git a/client/__init__.py b/client/__init__.py
1new file mode 10064483new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/__init__.py
diff --git a/client/main/__init__.py b/client/main/__init__.py
2new file mode 10064484new file mode 100644
index 0000000..0829681
--- /dev/null
+++ b/client/main/__init__.py
@@ -0,0 +1,3 @@
1__author__ = "Mateusz Susik"
2
3__all__ = ['mock', 'daemon']
diff --git a/client/main/client.py b/client/main/client.py
0new file mode 1006444new file mode 100644
index 0000000..82b62d1
--- /dev/null
+++ b/client/main/client.py
@@ -0,0 +1,164 @@
1# -*- coding: utf-8 -*-
2from __future__ import print_function
3from os import mkdir
4from os import path
5from random import sample
6from string import ascii_lowercase
7from string import ascii_uppercase
8from string import digits
9import subprocess
10import sys
11
12from requests import get
13from requests import post
14from requests import RequestException
15
16from ...systemcheck.systemcheck import systemcheck
17
18EXIT_OK = 0
19
20SERVER_ADDRESS = "http://private-a6e53-dpcs.apiary-mock.com/"
21
22
23def generate_report(exit_code, stderr_output):
24 """Generate a report which will be sent to the DCPS server.
25
26 Arguments
27 ---------
28 exit_code : int
29 The exit code of the failed process
30
31 stderr_output : string
32 The output written on stderr by the failed process
33
34 Return
35 ------
36 template : dict
37 The report that can be sent to the DCPS server.
38 """
39 template = {
40 "crash_report": {
41 "application": {
42 # TO DO - implementation depends on the way how the demon is
43 # implemented
44 },
45 "system_info": {},
46 "exit_code": exit_code,
47 "stderr_output": stderr_output
48 }
49 }
50
51 system_info = systemcheck()
52
53 for k, v in system_info['platform'].iteritems():
54 template['crash_report']['system_info'][k] = v
55
56 return template
57
58
59def handle_request_error(exception, dpcs_message):
60 """Handle the exception coming from the requests library.
61
62 Arguments
63 ---------
64 exception : requests.RequestException
65 The request exception caught during the communication with the server.
66 Can be anything from 310 to 500
67
68 dpcs_message : string
69 The message to be printed. Should describe why DPCS client failed.
70 """
71 print(dpcs_message, file=sys.stderr)
72 print(exception.response.text, file=sys.stderr)
73
74
75def rand_fname(parent_path, prefix, suffix, length=4):
76 """Create a random filename for storing the solution script.
77
78 Arguments
79 ---------
80 parent_path : string
81 The directory where the DCPS script should be stored.
82
83 prefix : string
84 The prefix that will be added to the filename.
85
86 suffix : string
87 The suffix that will be added to the filename.
88 Usually should end with '.sh'
89
90 length : int, optional
91 The length of the sequence of random
92 haracters in the created filename.
93
94 Return
95 ------
96 fname : string
97 The new file's name.
98 The file under this name is guaranteed not to exist.
99 """
100 chars = ascii_lowercase + ascii_uppercase + digits
101
102 fname = path.join(parent_path,
103 prefix + ''.join(sample(chars, length)) + suffix)
104
105 return fname if not path.exists(fname) else rand_fname(suffix, length)
106
107if __name__ == '__main__':
108
109 if len(sys.argv) != 2:
110 print("Usage python client.py '[command to check]'")
111 exit(1)
112
113 p = subprocess.Popen(sys.argv[1],
114 stdout=subprocess.PIPE,
115 stderr=subprocess.PIPE)
116 _, output = p.communicate()
117 code = p.returncode
118
119 if code != EXIT_OK:
120 # output to to, co wysyłamy
121
122 api_description_url = SERVER_ADDRESS + "vd1/paths/"
123 try:
124 response = get(api_description_url)
125 except RequestException as e:
126 handle_request_error(
127 e, "DPCS coudln't get api description"
128 )
129 exit(2)
130
131 api_paths = response.json()
132 crash_report_url = SERVER_ADDRESS + api_paths["crash-reports"]
133 headers = {
134 'Content-Type': 'text/json'
135 }
136
137 report = generate_report(code, output)
138
139 try:
140 response = post(crash_report_url,
141 headers=headers,
142 data=report)
143 except RequestException as e:
144 handle_request_error(
145 e, "DPCS coudln't post crash information"
146 )
147 exit(3)
148
149 script = response.json()['crash_report_ack'][
150 'solution']['shell_script']
151
152 home = path.expanduser("~")
153 scripts_directory = path.join(home, '.local/share/dpcs/')
154
155 if not path.exists(scripts_directory):
156 mkdir(scripts_directory)
157
158 filename = rand_fname(scripts_directory, 'dpcs_solution_', '.sh')
159
160 with open(filename, 'w+') as f:
161 f.write(script)
162
163 print("DPCS might have found a solution to your problem!")
164 print("The script produced by DPCS is available in " + filename)
diff --git a/client/main/demon.py b/client/main/demon.py
0new file mode 100644165new file mode 100644
index 0000000..c2e2768
--- /dev/null
+++ b/client/main/demon.py
@@ -0,0 +1,91 @@
1"""DPCS client implementation.
2
3Simplest usage:
4sudo python -m [parent_directory_name].demon start
5"""
6from __future__ import print_function
7from os import path
8import time
9
10from daemon import runner
11from requests import get
12from requests import post
13from requests import RequestException
14
15from .mock import sample_report
16
17
18SERVER_ADDRESS = "http://private-a6e53-dpcs.apiary-mock.com/"
19
20
21class DPCSDaemon():
22 """Wrapper over DPCS client functionality."""
23
24 def __init__(self):
25 self.stdin_path = '/dev/null'
26 self.stdout_path = '/dev/tty'
27 self.stderr_path = '/dev/tty'
28 self.pidfile_path = '/tmp/dpcs.pid'
29 self.pidfile_timeout = 5
30
31 def _handle_request_error(self, exception, dpcs_message):
32 print(dpcs_message)
33 print(exception.response.text)
34
35 def run(self):
36 first_sent = False
37
38 while True:
39
40 # Mock - sends every ten seconds a simple message.
41
42 # First report is sent immediately so that a developer
43 # doesn't have to wait
44 if first_sent:
45 time.sleep(10)
46 else:
47 first_sent = True
48
49 api_description_url = SERVER_ADDRESS + "vd1/paths/"
50 try:
51 response = get(api_description_url)
52 except RequestException as e:
53 self._handle_request_error(
54 e, "DPCS coudln't get api description"
55 )
56 continue
57
58 api_paths = response.json()
59 crash_report_url = SERVER_ADDRESS + api_paths["crash-reports"]
60 headers = {
61 'Content-Type': 'text/json'
62 }
63
64 try:
65 response = post(crash_report_url,
66 headers=headers,
67 data=sample_report)
68 except RequestException as e:
69 self._handle_request_error(
70 e, "DPCS coudln't post crash information"
71 )
72 continue
73
74 script = response.json()['crash_report_ack'][
75 'solution']['shell_script']
76
77 home = path.expanduser("~")
78 scripts_directory = path.join(home, '.local/share/dpcs/')
79
80 # Check if directory exists, create if not
81 # Create a script file (using unique id)
82
83 print("DPCS might have found a solution to your problem!")
84 print("The script produced by DPCS is available in ")
85 print(script)
86
87 print(home)
88
89app = DPCSDaemon()
90daemon_runner = runner.DaemonRunner(app)
91daemon_runner.do_action()
diff --git a/client/main/mock.py b/client/main/mock.py
0new file mode 10064492new file mode 100644
index 0000000..00577b2
--- /dev/null
+++ b/client/main/mock.py
@@ -0,0 +1,13 @@
1sample_report = {
2 "crash_report": {
3 "application": {
4 "name": "Google Chrome",
5 "version": "48.0.2564.116"
6 },
7 "system_info": {
8 "version": "14.04.1 LTS"
9 },
10 "exit_code": 1,
11 "stderr_output": "Lines from stdErr."
12 }
13}
diff --git a/systemcheck/__init__.py b/systemcheck/__init__.py
0new file mode 10064414new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/systemcheck/__init__.py
diff --git a/systemcheck/systemcheck.py b/systemcheck/systemcheck.py
index b7e4d8c..727ac75 100755
--- a/systemcheck/systemcheck.py
+++ b/systemcheck/systemcheck.py
@@ -5,7 +5,8 @@ import os
5import re5import re
6import json6import json
77
8if __name__ == "__main__":8
9def systemcheck():
9 uname = platform.uname()10 uname = platform.uname()
10 platform_dict = {11 platform_dict = {
11 "system_name": uname[1],12 "system_name": uname[1],
@@ -19,10 +20,11 @@ if __name__ == "__main__":
19 "dpkg-query -W -f='${binary:Package}\t${Version}\t${Status}\n' | grep \"install ok installed\""20 "dpkg-query -W -f='${binary:Package}\t${Version}\t${Status}\n' | grep \"install ok installed\""
20 ).read()21 ).read()
21 packages_raw_info = packages_raw_info.split("\n")22 packages_raw_info = packages_raw_info.split("\n")
23
24 # regex catches {binary:Package} and {Version} from dpkg output
25 package_re = re.compile(r"(.+)\t(.+)\t.*")
22 for line in packages_raw_info:26 for line in packages_raw_info:
23 if line:27 if line:
24 # regex catches {binary:Package} and {Version} from dpkg output
25 package_re = re.compile(r"(.+)\t(.+)\t.*")
26 package_info = package_re.match(line)28 package_info = package_re.match(line)
27 package_dict = {29 package_dict = {
28 "name": package_info.group(1),30 "name": package_info.group(1),
@@ -32,5 +34,9 @@ if __name__ == "__main__":
32 data = {}34 data = {}
33 data["platform"] = platform_dict35 data["platform"] = platform_dict
34 data["packages"] = package_list36 data["packages"] = package_list
35 json_data = json.dumps(data)37 return data
36 print json_data38
39
40if __name__ == "__main__":
41
42 print(json.dumps(systemcheck()))

Subscribers

People subscribed via source and target branches

to all changes: