1 | """Data structures for application/computer status.""" |
||
2 | |||
3 | 1 | import functools |
|
4 | 1 | import logging |
|
5 | |||
6 | 1 | import yorm |
|
0 ignored issues
–
show
|
|||
7 | |||
8 | 1 | from .timestamp import Timestamp |
|
9 | |||
10 | 1 | log = logging.getLogger(__name__) |
|
11 | |||
12 | |||
13 | 1 | def log_running(func): |
|
14 | """Decorator for methods that return application status.""" |
||
15 | 1 | @functools.wraps(func) |
|
16 | def wrapped(self, application, computer): |
||
17 | """Wrapped method to log if an application is running.""" |
||
18 | 1 | running = func(self, application, computer) |
|
19 | 1 | log.debug("%s marked as %s on: %s", |
|
20 | application, "started" if running else "stopped", computer) |
||
21 | 1 | return running |
|
22 | 1 | return wrapped |
|
23 | |||
24 | |||
25 | 1 | def log_starting(func): |
|
26 | """Decorator for methods that mark an application as started.""" |
||
27 | 1 | @functools.wraps(func) |
|
28 | def wrapped(self, application, computer): |
||
29 | """Wrapped method to log that an application is started.""" |
||
30 | 1 | log.debug("Marking %s as started on %s...", application, computer) |
|
31 | 1 | result = func(self, application, computer) |
|
32 | 1 | log.debug("%s marked as started on: %s", application, computer) |
|
33 | 1 | return result |
|
34 | 1 | return wrapped |
|
35 | |||
36 | |||
37 | 1 | def log_stopping(func): |
|
38 | """Decorator for methods that mark an application as stopped.""" |
||
39 | 1 | @functools.wraps(func) |
|
40 | def wrapped(self, application, computer): |
||
41 | """Wrapped method to log that an application is stopped.""" |
||
42 | 1 | log.debug("Marking %s as stopped on %s...", application, computer) |
|
43 | 1 | result = func(self, application, computer) |
|
44 | 1 | log.debug("%s marked as stopped on: %s", application, computer) |
|
45 | 1 | return result |
|
46 | 1 | return wrapped |
|
47 | |||
48 | |||
49 | 1 | @yorm.attr(computer=yorm.types.String) |
|
50 | 1 | @yorm.attr(timestamp=Timestamp) |
|
51 | 1 | class State(yorm.types.AttributeDictionary): |
|
52 | """Dictionary of computer state.""" |
||
53 | |||
54 | 1 | def __init__(self, computer, timestamp=None): |
|
55 | 1 | super().__init__() |
|
56 | 1 | self.computer = computer |
|
57 | 1 | self.timestamp = timestamp or Timestamp() |
|
58 | |||
59 | 1 | def __str__(self): |
|
60 | 1 | return str(self.computer) |
|
61 | |||
62 | 1 | def __lt__(self, other): |
|
63 | 1 | return str(self.computer).lower() < str(other.computer).lower() |
|
64 | |||
65 | |||
66 | 1 | @yorm.attr(all=State) |
|
0 ignored issues
–
show
|
|||
67 | 1 | class StateList(yorm.types.SortedList): |
|
68 | """List of computer states for an application.""" |
||
69 | |||
70 | |||
71 | 1 | @yorm.attr(application=yorm.types.String) |
|
72 | 1 | @yorm.attr(computers=StateList) |
|
73 | 1 | @yorm.attr(next=yorm.types.NullableString) |
|
74 | 1 | class Status(yorm.types.AttributeDictionary): |
|
75 | """Dictionary of computers using an application.""" |
||
76 | |||
77 | 1 | def __init__(self, application, computers=None, next=None): # pylint: disable=redefined-builtin |
|
78 | 1 | super().__init__() |
|
79 | 1 | self.application = application |
|
80 | 1 | self.computers = computers or StateList() |
|
81 | 1 | self.next = next |
|
82 | |||
83 | 1 | def __str__(self): |
|
84 | 1 | return str(self.application) |
|
85 | |||
86 | 1 | def __lt__(self, other): |
|
87 | 1 | return str(self.application).lower() < str(other.application).lower() |
|
88 | |||
89 | |||
90 | 1 | @yorm.attr(all=Status) |
|
0 ignored issues
–
show
|
|||
91 | 1 | class StatusList(yorm.types.SortedList): |
|
92 | """List of application statuses.""" |
||
93 | |||
94 | |||
95 | 1 | @yorm.attr(applications=StatusList) |
|
96 | 1 | @yorm.attr(counter=yorm.types.Integer) |
|
97 | 1 | class ProgramStatus(yorm.types.AttributeDictionary): |
|
98 | """Dictionary of current program status.""" |
||
99 | |||
100 | 1 | def __init__(self, applications=None, counter=0): |
|
101 | 1 | super().__init__() |
|
102 | 1 | self.applications = applications or StatusList() |
|
103 | 1 | self.counter = counter |
|
104 | |||
105 | 1 | def find(self, application): |
|
106 | """Return the application status for an application.""" |
||
107 | 1 | for app_status in self.applications: |
|
108 | 1 | if app_status.application == application.name: |
|
109 | 1 | break |
|
110 | else: |
||
111 | 1 | app_status = Status(application.name) |
|
112 | 1 | self.applications.append(app_status) |
|
113 | 1 | return app_status |
|
114 | |||
115 | 1 | def get_latest(self, application): |
|
116 | """Get the last computer's name logged as running an application.""" |
||
117 | 1 | for status in self.applications: |
|
118 | 1 | if status.application == application.name: |
|
119 | 1 | states = [s for s in status.computers if s.timestamp.active] |
|
120 | 1 | if states: |
|
121 | 1 | states.sort(key=lambda s: s.timestamp, reverse=True) |
|
122 | 1 | log.debug("%s marked as started on: %s", application, |
|
123 | ', '.join(str(s) for s in states)) |
||
124 | # TODO: consider returning the computer instance? |
||
125 | 1 | return states[0].computer |
|
126 | |||
127 | 1 | log.debug("marked as started on: nothing") |
|
128 | 1 | return None |
|
129 | |||
130 | 1 | @log_running |
|
131 | def is_running(self, application, computer): |
||
132 | """Determine if an application is logged as running on a computer.""" |
||
133 | 1 | for status in self.applications: |
|
134 | 1 | if status.application == application.name: |
|
135 | 1 | for state in status.computers: |
|
136 | 1 | if state.computer == computer.name: |
|
137 | 1 | return state.timestamp.active |
|
138 | |||
139 | # Status not found, assume the application is not running |
||
140 | 1 | return False |
|
141 | |||
142 | 1 | def queue(self, application, computer): |
|
143 | """Record an application as queued for launch on a computer.""" |
||
144 | 1 | status = self.find(application) |
|
145 | 1 | status.next = computer.name |
|
146 | |||
147 | 1 | @log_starting |
|
148 | def start(self, application, computer): |
||
149 | """Record an application as running on a computer.""" |
||
150 | 1 | for status in self.applications: |
|
151 | 1 | if status.application == application.name: |
|
152 | 1 | for state in status.computers: |
|
153 | 1 | if state.computer == computer.name: |
|
154 | 1 | self.counter += 1 |
|
155 | 1 | state.timestamp.started = self.counter |
|
156 | 1 | return |
|
157 | 1 | break |
|
158 | else: |
||
159 | 1 | status = None |
|
160 | |||
161 | # Status not found, add the application/computer as started |
||
162 | 1 | self.counter += 1 |
|
163 | 1 | state = State(computer.name) |
|
164 | 1 | state.timestamp.started = self.counter |
|
165 | 1 | if status is None: |
|
166 | 1 | status = Status(application.name) |
|
167 | 1 | status.computers.append(state) |
|
168 | 1 | self.applications.append(status) |
|
169 | else: |
||
170 | 1 | status.computers.append(state) |
|
171 | |||
172 | 1 | @log_stopping |
|
173 | def stop(self, application, computer): |
||
174 | """Record an application as no longer running on a computer.""" |
||
175 | 1 | for status in self.applications: |
|
176 | 1 | if status.application == application.name: |
|
177 | 1 | for state in status.computers: |
|
178 | 1 | if state.computer == computer.name: |
|
179 | 1 | self.counter += 1 |
|
180 | 1 | state.timestamp.stopped = self.counter |
|
181 | 1 | return |
|
182 | 1 | break |
|
183 | else: |
||
184 | 1 | status = None |
|
185 | |||
186 | # Status not found, add the application/computer as stopped |
||
187 | 1 | self.counter += 1 |
|
188 | 1 | state = State(computer.name) |
|
189 | 1 | state.timestamp.stopped = self.counter |
|
190 | 1 | if status is None: |
|
191 | 1 | status = Status(application.name) |
|
192 | 1 | status.computers.append(state) |
|
193 | 1 | self.applications.append(status) |
|
194 | else: |
||
195 | status.computers.append(state) |
||
196 |
This can be caused by one of the following:
1. Missing Dependencies
This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.
2. Missing __init__.py files
This error could also result from missing
__init__.py
files in your module folders. Make sure that you place one file in each sub-folder.