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
1diff --git a/.gitignore b/.gitignore
2new file mode 100644
3index 0000000..908a064
4--- /dev/null
5+++ b/.gitignore
6@@ -0,0 +1,81 @@
7+# Byte-compiled / optimized / DLL files
8+__pycache__/
9+*.py[cod]
10+*$py.class
11+
12+# C extensions
13+*.so
14+
15+# Distribution / packaging
16+.Python
17+env/
18+build/
19+develop-eggs/
20+dist/
21+downloads/
22+eggs/
23+.eggs/
24+lib/
25+lib64/
26+parts/
27+sdist/
28+var/
29+*.egg-info/
30+.installed.cfg
31+*.egg
32+
33+# PyInstaller
34+# Usually these files are written by a python script from a template
35+# before PyInstaller builds the exe, so as to inject date/other infos into it.
36+*.manifest
37+*.spec
38+
39+# Installer logs
40+pip-log.txt
41+pip-delete-this-directory.txt
42+
43+# Unit test / coverage reports
44+htmlcov/
45+.tox/
46+.coverage
47+.coverage.*
48+.cache
49+nosetests.xml
50+coverage.xml
51+*,cover
52+.hypothesis/
53+
54+# Translations
55+*.mo
56+*.pot
57+
58+# Django stuff:
59+*.log
60+local_settings.py
61+
62+# Flask instance folder
63+instance/
64+
65+# Scrapy stuff:
66+.scrapy
67+
68+# Sphinx documentation
69+docs/_build/
70+
71+# PyBuilder
72+target/
73+
74+# IPython Notebook
75+.ipynb_checkpoints
76+
77+# pyenv
78+.python-version
79+
80+# celery beat schedule file
81+celerybeat-schedule
82+
83+# dotenv
84+.env
85+
86+# Spyder project settings
87+.spyderproject
88diff --git a/__init__.py b/__init__.py
89new file mode 100644
90index 0000000..e69de29
91--- /dev/null
92+++ b/__init__.py
93diff --git a/client/__init__.py b/client/__init__.py
94new file mode 100644
95index 0000000..e69de29
96--- /dev/null
97+++ b/client/__init__.py
98diff --git a/client/main/__init__.py b/client/main/__init__.py
99new file mode 100644
100index 0000000..0829681
101--- /dev/null
102+++ b/client/main/__init__.py
103@@ -0,0 +1,3 @@
104+__author__ = "Mateusz Susik"
105+
106+__all__ = ['mock', 'daemon']
107diff --git a/client/main/client.py b/client/main/client.py
108new file mode 100644
109index 0000000..82b62d1
110--- /dev/null
111+++ b/client/main/client.py
112@@ -0,0 +1,164 @@
113+# -*- coding: utf-8 -*-
114+from __future__ import print_function
115+from os import mkdir
116+from os import path
117+from random import sample
118+from string import ascii_lowercase
119+from string import ascii_uppercase
120+from string import digits
121+import subprocess
122+import sys
123+
124+from requests import get
125+from requests import post
126+from requests import RequestException
127+
128+from ...systemcheck.systemcheck import systemcheck
129+
130+EXIT_OK = 0
131+
132+SERVER_ADDRESS = "http://private-a6e53-dpcs.apiary-mock.com/"
133+
134+
135+def generate_report(exit_code, stderr_output):
136+ """Generate a report which will be sent to the DCPS server.
137+
138+ Arguments
139+ ---------
140+ exit_code : int
141+ The exit code of the failed process
142+
143+ stderr_output : string
144+ The output written on stderr by the failed process
145+
146+ Return
147+ ------
148+ template : dict
149+ The report that can be sent to the DCPS server.
150+ """
151+ template = {
152+ "crash_report": {
153+ "application": {
154+ # TO DO - implementation depends on the way how the demon is
155+ # implemented
156+ },
157+ "system_info": {},
158+ "exit_code": exit_code,
159+ "stderr_output": stderr_output
160+ }
161+ }
162+
163+ system_info = systemcheck()
164+
165+ for k, v in system_info['platform'].iteritems():
166+ template['crash_report']['system_info'][k] = v
167+
168+ return template
169+
170+
171+def handle_request_error(exception, dpcs_message):
172+ """Handle the exception coming from the requests library.
173+
174+ Arguments
175+ ---------
176+ exception : requests.RequestException
177+ The request exception caught during the communication with the server.
178+ Can be anything from 310 to 500
179+
180+ dpcs_message : string
181+ The message to be printed. Should describe why DPCS client failed.
182+ """
183+ print(dpcs_message, file=sys.stderr)
184+ print(exception.response.text, file=sys.stderr)
185+
186+
187+def rand_fname(parent_path, prefix, suffix, length=4):
188+ """Create a random filename for storing the solution script.
189+
190+ Arguments
191+ ---------
192+ parent_path : string
193+ The directory where the DCPS script should be stored.
194+
195+ prefix : string
196+ The prefix that will be added to the filename.
197+
198+ suffix : string
199+ The suffix that will be added to the filename.
200+ Usually should end with '.sh'
201+
202+ length : int, optional
203+ The length of the sequence of random
204+ haracters in the created filename.
205+
206+ Return
207+ ------
208+ fname : string
209+ The new file's name.
210+ The file under this name is guaranteed not to exist.
211+ """
212+ chars = ascii_lowercase + ascii_uppercase + digits
213+
214+ fname = path.join(parent_path,
215+ prefix + ''.join(sample(chars, length)) + suffix)
216+
217+ return fname if not path.exists(fname) else rand_fname(suffix, length)
218+
219+if __name__ == '__main__':
220+
221+ if len(sys.argv) != 2:
222+ print("Usage python client.py '[command to check]'")
223+ exit(1)
224+
225+ p = subprocess.Popen(sys.argv[1],
226+ stdout=subprocess.PIPE,
227+ stderr=subprocess.PIPE)
228+ _, output = p.communicate()
229+ code = p.returncode
230+
231+ if code != EXIT_OK:
232+ # output to to, co wysyłamy
233+
234+ api_description_url = SERVER_ADDRESS + "vd1/paths/"
235+ try:
236+ response = get(api_description_url)
237+ except RequestException as e:
238+ handle_request_error(
239+ e, "DPCS coudln't get api description"
240+ )
241+ exit(2)
242+
243+ api_paths = response.json()
244+ crash_report_url = SERVER_ADDRESS + api_paths["crash-reports"]
245+ headers = {
246+ 'Content-Type': 'text/json'
247+ }
248+
249+ report = generate_report(code, output)
250+
251+ try:
252+ response = post(crash_report_url,
253+ headers=headers,
254+ data=report)
255+ except RequestException as e:
256+ handle_request_error(
257+ e, "DPCS coudln't post crash information"
258+ )
259+ exit(3)
260+
261+ script = response.json()['crash_report_ack'][
262+ 'solution']['shell_script']
263+
264+ home = path.expanduser("~")
265+ scripts_directory = path.join(home, '.local/share/dpcs/')
266+
267+ if not path.exists(scripts_directory):
268+ mkdir(scripts_directory)
269+
270+ filename = rand_fname(scripts_directory, 'dpcs_solution_', '.sh')
271+
272+ with open(filename, 'w+') as f:
273+ f.write(script)
274+
275+ print("DPCS might have found a solution to your problem!")
276+ print("The script produced by DPCS is available in " + filename)
277diff --git a/client/main/demon.py b/client/main/demon.py
278new file mode 100644
279index 0000000..c2e2768
280--- /dev/null
281+++ b/client/main/demon.py
282@@ -0,0 +1,91 @@
283+"""DPCS client implementation.
284+
285+Simplest usage:
286+sudo python -m [parent_directory_name].demon start
287+"""
288+from __future__ import print_function
289+from os import path
290+import time
291+
292+from daemon import runner
293+from requests import get
294+from requests import post
295+from requests import RequestException
296+
297+from .mock import sample_report
298+
299+
300+SERVER_ADDRESS = "http://private-a6e53-dpcs.apiary-mock.com/"
301+
302+
303+class DPCSDaemon():
304+ """Wrapper over DPCS client functionality."""
305+
306+ def __init__(self):
307+ self.stdin_path = '/dev/null'
308+ self.stdout_path = '/dev/tty'
309+ self.stderr_path = '/dev/tty'
310+ self.pidfile_path = '/tmp/dpcs.pid'
311+ self.pidfile_timeout = 5
312+
313+ def _handle_request_error(self, exception, dpcs_message):
314+ print(dpcs_message)
315+ print(exception.response.text)
316+
317+ def run(self):
318+ first_sent = False
319+
320+ while True:
321+
322+ # Mock - sends every ten seconds a simple message.
323+
324+ # First report is sent immediately so that a developer
325+ # doesn't have to wait
326+ if first_sent:
327+ time.sleep(10)
328+ else:
329+ first_sent = True
330+
331+ api_description_url = SERVER_ADDRESS + "vd1/paths/"
332+ try:
333+ response = get(api_description_url)
334+ except RequestException as e:
335+ self._handle_request_error(
336+ e, "DPCS coudln't get api description"
337+ )
338+ continue
339+
340+ api_paths = response.json()
341+ crash_report_url = SERVER_ADDRESS + api_paths["crash-reports"]
342+ headers = {
343+ 'Content-Type': 'text/json'
344+ }
345+
346+ try:
347+ response = post(crash_report_url,
348+ headers=headers,
349+ data=sample_report)
350+ except RequestException as e:
351+ self._handle_request_error(
352+ e, "DPCS coudln't post crash information"
353+ )
354+ continue
355+
356+ script = response.json()['crash_report_ack'][
357+ 'solution']['shell_script']
358+
359+ home = path.expanduser("~")
360+ scripts_directory = path.join(home, '.local/share/dpcs/')
361+
362+ # Check if directory exists, create if not
363+ # Create a script file (using unique id)
364+
365+ print("DPCS might have found a solution to your problem!")
366+ print("The script produced by DPCS is available in ")
367+ print(script)
368+
369+ print(home)
370+
371+app = DPCSDaemon()
372+daemon_runner = runner.DaemonRunner(app)
373+daemon_runner.do_action()
374diff --git a/client/main/mock.py b/client/main/mock.py
375new file mode 100644
376index 0000000..00577b2
377--- /dev/null
378+++ b/client/main/mock.py
379@@ -0,0 +1,13 @@
380+sample_report = {
381+ "crash_report": {
382+ "application": {
383+ "name": "Google Chrome",
384+ "version": "48.0.2564.116"
385+ },
386+ "system_info": {
387+ "version": "14.04.1 LTS"
388+ },
389+ "exit_code": 1,
390+ "stderr_output": "Lines from stdErr."
391+ }
392+}
393diff --git a/systemcheck/__init__.py b/systemcheck/__init__.py
394new file mode 100644
395index 0000000..e69de29
396--- /dev/null
397+++ b/systemcheck/__init__.py
398diff --git a/systemcheck/systemcheck.py b/systemcheck/systemcheck.py
399index b7e4d8c..727ac75 100755
400--- a/systemcheck/systemcheck.py
401+++ b/systemcheck/systemcheck.py
402@@ -5,7 +5,8 @@ import os
403 import re
404 import json
405
406-if __name__ == "__main__":
407+
408+def systemcheck():
409 uname = platform.uname()
410 platform_dict = {
411 "system_name": uname[1],
412@@ -19,10 +20,11 @@ if __name__ == "__main__":
413 "dpkg-query -W -f='${binary:Package}\t${Version}\t${Status}\n' | grep \"install ok installed\""
414 ).read()
415 packages_raw_info = packages_raw_info.split("\n")
416+
417+ # regex catches {binary:Package} and {Version} from dpkg output
418+ package_re = re.compile(r"(.+)\t(.+)\t.*")
419 for line in packages_raw_info:
420 if line:
421- # regex catches {binary:Package} and {Version} from dpkg output
422- package_re = re.compile(r"(.+)\t(.+)\t.*")
423 package_info = package_re.match(line)
424 package_dict = {
425 "name": package_info.group(1),
426@@ -32,5 +34,9 @@ if __name__ == "__main__":
427 data = {}
428 data["platform"] = platform_dict
429 data["packages"] = package_list
430- json_data = json.dumps(data)
431- print json_data
432+ return data
433+
434+
435+if __name__ == "__main__":
436+
437+ print(json.dumps(systemcheck()))

Subscribers

People subscribed via source and target branches

to all changes: