Merge lp:~stub/charms/precise/postgresql-psql/trunk into lp:charms/postgresql-psql
- Precise Pangolin (12.04)
- trunk
- Merge into trunk
Proposed by
Stuart Bishop
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Stuart Bishop | ||||||||||||
Approved revision: | 20 | ||||||||||||
Merged at revision: | 16 | ||||||||||||
Proposed branch: | lp:~stub/charms/precise/postgresql-psql/trunk | ||||||||||||
Merge into: | lp:charms/postgresql-psql | ||||||||||||
Diff against target: |
1261 lines (+994/-171) 5 files modified
charm-helpers.yaml (+4/-0) hooks/charmhelpers/core/hookenv.py (+339/-0) hooks/charmhelpers/core/host.py (+272/-0) hooks/hooks.py (+36/-94) icon.svg (+343/-77) |
||||||||||||
To merge this branch: | bzr merge lp:~stub/charms/precise/postgresql-psql/trunk | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Kapil Thangavelu (community) | Approve | ||
Review via email: mp+174773@code.launchpad.net |
Commit message
Wait until the new 'allowed-units' property tells us permissions have been granted before creating database access scripts.
Description of the change
The first change on this branch is to make use of the 'allowed-units' setting on the relation, allowing us to confirm that https:/
The bulk of the changes is tearing out our helpers and using charm-helpers instead. The included version of charm-helpers is current trunk.
To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote : | # |
- 20. By Stuart Bishop
-
New icon using template
Revision history for this message
Stuart Bishop (stub) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'charm-helpers.yaml' | |||
2 | --- charm-helpers.yaml 1970-01-01 00:00:00 +0000 | |||
3 | +++ charm-helpers.yaml 2013-07-16 12:39:27 +0000 | |||
4 | @@ -0,0 +1,4 @@ | |||
5 | 1 | destination: hooks/charmhelpers | ||
6 | 2 | branch: lp:charm-helpers | ||
7 | 3 | include: | ||
8 | 4 | - core | ||
9 | 0 | 5 | ||
10 | === added directory 'hooks/charmhelpers' | |||
11 | === added file 'hooks/charmhelpers/__init__.py' | |||
12 | === added directory 'hooks/charmhelpers/core' | |||
13 | === added file 'hooks/charmhelpers/core/__init__.py' | |||
14 | === added file 'hooks/charmhelpers/core/hookenv.py' | |||
15 | --- hooks/charmhelpers/core/hookenv.py 1970-01-01 00:00:00 +0000 | |||
16 | +++ hooks/charmhelpers/core/hookenv.py 2013-07-16 12:39:27 +0000 | |||
17 | @@ -0,0 +1,339 @@ | |||
18 | 1 | "Interactions with the Juju environment" | ||
19 | 2 | # Copyright 2013 Canonical Ltd. | ||
20 | 3 | # | ||
21 | 4 | # Authors: | ||
22 | 5 | # Charm Helpers Developers <juju@lists.ubuntu.com> | ||
23 | 6 | |||
24 | 7 | import os | ||
25 | 8 | import json | ||
26 | 9 | import yaml | ||
27 | 10 | import subprocess | ||
28 | 11 | import UserDict | ||
29 | 12 | |||
30 | 13 | CRITICAL = "CRITICAL" | ||
31 | 14 | ERROR = "ERROR" | ||
32 | 15 | WARNING = "WARNING" | ||
33 | 16 | INFO = "INFO" | ||
34 | 17 | DEBUG = "DEBUG" | ||
35 | 18 | MARKER = object() | ||
36 | 19 | |||
37 | 20 | cache = {} | ||
38 | 21 | |||
39 | 22 | |||
40 | 23 | def cached(func): | ||
41 | 24 | ''' Cache return values for multiple executions of func + args | ||
42 | 25 | |||
43 | 26 | For example: | ||
44 | 27 | |||
45 | 28 | @cached | ||
46 | 29 | def unit_get(attribute): | ||
47 | 30 | pass | ||
48 | 31 | |||
49 | 32 | unit_get('test') | ||
50 | 33 | |||
51 | 34 | will cache the result of unit_get + 'test' for future calls. | ||
52 | 35 | ''' | ||
53 | 36 | def wrapper(*args, **kwargs): | ||
54 | 37 | global cache | ||
55 | 38 | key = str((func, args, kwargs)) | ||
56 | 39 | try: | ||
57 | 40 | return cache[key] | ||
58 | 41 | except KeyError: | ||
59 | 42 | res = func(*args, **kwargs) | ||
60 | 43 | cache[key] = res | ||
61 | 44 | return res | ||
62 | 45 | return wrapper | ||
63 | 46 | |||
64 | 47 | |||
65 | 48 | def flush(key): | ||
66 | 49 | ''' Flushes any entries from function cache where the | ||
67 | 50 | key is found in the function+args ''' | ||
68 | 51 | flush_list = [] | ||
69 | 52 | for item in cache: | ||
70 | 53 | if key in item: | ||
71 | 54 | flush_list.append(item) | ||
72 | 55 | for item in flush_list: | ||
73 | 56 | del cache[item] | ||
74 | 57 | |||
75 | 58 | |||
76 | 59 | def log(message, level=None): | ||
77 | 60 | "Write a message to the juju log" | ||
78 | 61 | command = ['juju-log'] | ||
79 | 62 | if level: | ||
80 | 63 | command += ['-l', level] | ||
81 | 64 | command += [message] | ||
82 | 65 | subprocess.call(command) | ||
83 | 66 | |||
84 | 67 | |||
85 | 68 | class Serializable(UserDict.IterableUserDict): | ||
86 | 69 | "Wrapper, an object that can be serialized to yaml or json" | ||
87 | 70 | |||
88 | 71 | def __init__(self, obj): | ||
89 | 72 | # wrap the object | ||
90 | 73 | UserDict.IterableUserDict.__init__(self) | ||
91 | 74 | self.data = obj | ||
92 | 75 | |||
93 | 76 | def __getattr__(self, attr): | ||
94 | 77 | # See if this object has attribute. | ||
95 | 78 | if attr in ("json", "yaml", "data"): | ||
96 | 79 | return self.__dict__[attr] | ||
97 | 80 | # Check for attribute in wrapped object. | ||
98 | 81 | got = getattr(self.data, attr, MARKER) | ||
99 | 82 | if got is not MARKER: | ||
100 | 83 | return got | ||
101 | 84 | # Proxy to the wrapped object via dict interface. | ||
102 | 85 | try: | ||
103 | 86 | return self.data[attr] | ||
104 | 87 | except KeyError: | ||
105 | 88 | raise AttributeError(attr) | ||
106 | 89 | |||
107 | 90 | def __getstate__(self): | ||
108 | 91 | # Pickle as a standard dictionary. | ||
109 | 92 | return self.data | ||
110 | 93 | |||
111 | 94 | def __setstate__(self, state): | ||
112 | 95 | # Unpickle into our wrapper. | ||
113 | 96 | self.data = state | ||
114 | 97 | |||
115 | 98 | def json(self): | ||
116 | 99 | "Serialize the object to json" | ||
117 | 100 | return json.dumps(self.data) | ||
118 | 101 | |||
119 | 102 | def yaml(self): | ||
120 | 103 | "Serialize the object to yaml" | ||
121 | 104 | return yaml.dump(self.data) | ||
122 | 105 | |||
123 | 106 | |||
124 | 107 | def execution_environment(): | ||
125 | 108 | """A convenient bundling of the current execution context""" | ||
126 | 109 | context = {} | ||
127 | 110 | context['conf'] = config() | ||
128 | 111 | if relation_id(): | ||
129 | 112 | context['reltype'] = relation_type() | ||
130 | 113 | context['relid'] = relation_id() | ||
131 | 114 | context['rel'] = relation_get() | ||
132 | 115 | context['unit'] = local_unit() | ||
133 | 116 | context['rels'] = relations() | ||
134 | 117 | context['env'] = os.environ | ||
135 | 118 | return context | ||
136 | 119 | |||
137 | 120 | |||
138 | 121 | def in_relation_hook(): | ||
139 | 122 | "Determine whether we're running in a relation hook" | ||
140 | 123 | return 'JUJU_RELATION' in os.environ | ||
141 | 124 | |||
142 | 125 | |||
143 | 126 | def relation_type(): | ||
144 | 127 | "The scope for the current relation hook" | ||
145 | 128 | return os.environ.get('JUJU_RELATION', None) | ||
146 | 129 | |||
147 | 130 | |||
148 | 131 | def relation_id(): | ||
149 | 132 | "The relation ID for the current relation hook" | ||
150 | 133 | return os.environ.get('JUJU_RELATION_ID', None) | ||
151 | 134 | |||
152 | 135 | |||
153 | 136 | def local_unit(): | ||
154 | 137 | "Local unit ID" | ||
155 | 138 | return os.environ['JUJU_UNIT_NAME'] | ||
156 | 139 | |||
157 | 140 | |||
158 | 141 | def remote_unit(): | ||
159 | 142 | "The remote unit for the current relation hook" | ||
160 | 143 | return os.environ['JUJU_REMOTE_UNIT'] | ||
161 | 144 | |||
162 | 145 | |||
163 | 146 | def service_name(): | ||
164 | 147 | "The name service group this unit belongs to" | ||
165 | 148 | return local_unit().split('/')[0] | ||
166 | 149 | |||
167 | 150 | |||
168 | 151 | @cached | ||
169 | 152 | def config(scope=None): | ||
170 | 153 | "Juju charm configuration" | ||
171 | 154 | config_cmd_line = ['config-get'] | ||
172 | 155 | if scope is not None: | ||
173 | 156 | config_cmd_line.append(scope) | ||
174 | 157 | config_cmd_line.append('--format=json') | ||
175 | 158 | try: | ||
176 | 159 | return json.loads(subprocess.check_output(config_cmd_line)) | ||
177 | 160 | except ValueError: | ||
178 | 161 | return None | ||
179 | 162 | |||
180 | 163 | |||
181 | 164 | @cached | ||
182 | 165 | def relation_get(attribute=None, unit=None, rid=None): | ||
183 | 166 | _args = ['relation-get', '--format=json'] | ||
184 | 167 | if rid: | ||
185 | 168 | _args.append('-r') | ||
186 | 169 | _args.append(rid) | ||
187 | 170 | _args.append(attribute or '-') | ||
188 | 171 | if unit: | ||
189 | 172 | _args.append(unit) | ||
190 | 173 | try: | ||
191 | 174 | return json.loads(subprocess.check_output(_args)) | ||
192 | 175 | except ValueError: | ||
193 | 176 | return None | ||
194 | 177 | |||
195 | 178 | |||
196 | 179 | def relation_set(relation_id=None, relation_settings={}, **kwargs): | ||
197 | 180 | relation_cmd_line = ['relation-set'] | ||
198 | 181 | if relation_id is not None: | ||
199 | 182 | relation_cmd_line.extend(('-r', relation_id)) | ||
200 | 183 | for k, v in (relation_settings.items() + kwargs.items()): | ||
201 | 184 | if v is None: | ||
202 | 185 | relation_cmd_line.append('{}='.format(k)) | ||
203 | 186 | else: | ||
204 | 187 | relation_cmd_line.append('{}={}'.format(k, v)) | ||
205 | 188 | subprocess.check_call(relation_cmd_line) | ||
206 | 189 | # Flush cache of any relation-gets for local unit | ||
207 | 190 | flush(local_unit()) | ||
208 | 191 | |||
209 | 192 | |||
210 | 193 | @cached | ||
211 | 194 | def relation_ids(reltype=None): | ||
212 | 195 | "A list of relation_ids" | ||
213 | 196 | reltype = reltype or relation_type() | ||
214 | 197 | relid_cmd_line = ['relation-ids', '--format=json'] | ||
215 | 198 | if reltype is not None: | ||
216 | 199 | relid_cmd_line.append(reltype) | ||
217 | 200 | return json.loads(subprocess.check_output(relid_cmd_line)) or [] | ||
218 | 201 | return [] | ||
219 | 202 | |||
220 | 203 | |||
221 | 204 | @cached | ||
222 | 205 | def related_units(relid=None): | ||
223 | 206 | "A list of related units" | ||
224 | 207 | relid = relid or relation_id() | ||
225 | 208 | units_cmd_line = ['relation-list', '--format=json'] | ||
226 | 209 | if relid is not None: | ||
227 | 210 | units_cmd_line.extend(('-r', relid)) | ||
228 | 211 | return json.loads(subprocess.check_output(units_cmd_line)) or [] | ||
229 | 212 | |||
230 | 213 | |||
231 | 214 | @cached | ||
232 | 215 | def relation_for_unit(unit=None, rid=None): | ||
233 | 216 | "Get the json represenation of a unit's relation" | ||
234 | 217 | unit = unit or remote_unit() | ||
235 | 218 | relation = relation_get(unit=unit, rid=rid) | ||
236 | 219 | for key in relation: | ||
237 | 220 | if key.endswith('-list'): | ||
238 | 221 | relation[key] = relation[key].split() | ||
239 | 222 | relation['__unit__'] = unit | ||
240 | 223 | return relation | ||
241 | 224 | |||
242 | 225 | |||
243 | 226 | @cached | ||
244 | 227 | def relations_for_id(relid=None): | ||
245 | 228 | "Get relations of a specific relation ID" | ||
246 | 229 | relation_data = [] | ||
247 | 230 | relid = relid or relation_ids() | ||
248 | 231 | for unit in related_units(relid): | ||
249 | 232 | unit_data = relation_for_unit(unit, relid) | ||
250 | 233 | unit_data['__relid__'] = relid | ||
251 | 234 | relation_data.append(unit_data) | ||
252 | 235 | return relation_data | ||
253 | 236 | |||
254 | 237 | |||
255 | 238 | @cached | ||
256 | 239 | def relations_of_type(reltype=None): | ||
257 | 240 | "Get relations of a specific type" | ||
258 | 241 | relation_data = [] | ||
259 | 242 | reltype = reltype or relation_type() | ||
260 | 243 | for relid in relation_ids(reltype): | ||
261 | 244 | for relation in relations_for_id(relid): | ||
262 | 245 | relation['__relid__'] = relid | ||
263 | 246 | relation_data.append(relation) | ||
264 | 247 | return relation_data | ||
265 | 248 | |||
266 | 249 | |||
267 | 250 | @cached | ||
268 | 251 | def relation_types(): | ||
269 | 252 | "Get a list of relation types supported by this charm" | ||
270 | 253 | charmdir = os.environ.get('CHARM_DIR', '') | ||
271 | 254 | mdf = open(os.path.join(charmdir, 'metadata.yaml')) | ||
272 | 255 | md = yaml.safe_load(mdf) | ||
273 | 256 | rel_types = [] | ||
274 | 257 | for key in ('provides', 'requires', 'peers'): | ||
275 | 258 | section = md.get(key) | ||
276 | 259 | if section: | ||
277 | 260 | rel_types.extend(section.keys()) | ||
278 | 261 | mdf.close() | ||
279 | 262 | return rel_types | ||
280 | 263 | |||
281 | 264 | |||
282 | 265 | @cached | ||
283 | 266 | def relations(): | ||
284 | 267 | rels = {} | ||
285 | 268 | for reltype in relation_types(): | ||
286 | 269 | relids = {} | ||
287 | 270 | for relid in relation_ids(reltype): | ||
288 | 271 | units = {local_unit(): relation_get(unit=local_unit(), rid=relid)} | ||
289 | 272 | for unit in related_units(relid): | ||
290 | 273 | reldata = relation_get(unit=unit, rid=relid) | ||
291 | 274 | units[unit] = reldata | ||
292 | 275 | relids[relid] = units | ||
293 | 276 | rels[reltype] = relids | ||
294 | 277 | return rels | ||
295 | 278 | |||
296 | 279 | |||
297 | 280 | def open_port(port, protocol="TCP"): | ||
298 | 281 | "Open a service network port" | ||
299 | 282 | _args = ['open-port'] | ||
300 | 283 | _args.append('{}/{}'.format(port, protocol)) | ||
301 | 284 | subprocess.check_call(_args) | ||
302 | 285 | |||
303 | 286 | |||
304 | 287 | def close_port(port, protocol="TCP"): | ||
305 | 288 | "Close a service network port" | ||
306 | 289 | _args = ['close-port'] | ||
307 | 290 | _args.append('{}/{}'.format(port, protocol)) | ||
308 | 291 | subprocess.check_call(_args) | ||
309 | 292 | |||
310 | 293 | |||
311 | 294 | @cached | ||
312 | 295 | def unit_get(attribute): | ||
313 | 296 | _args = ['unit-get', '--format=json', attribute] | ||
314 | 297 | try: | ||
315 | 298 | return json.loads(subprocess.check_output(_args)) | ||
316 | 299 | except ValueError: | ||
317 | 300 | return None | ||
318 | 301 | |||
319 | 302 | |||
320 | 303 | def unit_private_ip(): | ||
321 | 304 | return unit_get('private-address') | ||
322 | 305 | |||
323 | 306 | |||
324 | 307 | class UnregisteredHookError(Exception): | ||
325 | 308 | pass | ||
326 | 309 | |||
327 | 310 | |||
328 | 311 | class Hooks(object): | ||
329 | 312 | def __init__(self): | ||
330 | 313 | super(Hooks, self).__init__() | ||
331 | 314 | self._hooks = {} | ||
332 | 315 | |||
333 | 316 | def register(self, name, function): | ||
334 | 317 | self._hooks[name] = function | ||
335 | 318 | |||
336 | 319 | def execute(self, args): | ||
337 | 320 | hook_name = os.path.basename(args[0]) | ||
338 | 321 | if hook_name in self._hooks: | ||
339 | 322 | self._hooks[hook_name]() | ||
340 | 323 | else: | ||
341 | 324 | raise UnregisteredHookError(hook_name) | ||
342 | 325 | |||
343 | 326 | def hook(self, *hook_names): | ||
344 | 327 | def wrapper(decorated): | ||
345 | 328 | for hook_name in hook_names: | ||
346 | 329 | self.register(hook_name, decorated) | ||
347 | 330 | else: | ||
348 | 331 | self.register(decorated.__name__, decorated) | ||
349 | 332 | if '_' in decorated.__name__: | ||
350 | 333 | self.register( | ||
351 | 334 | decorated.__name__.replace('_', '-'), decorated) | ||
352 | 335 | return decorated | ||
353 | 336 | return wrapper | ||
354 | 337 | |||
355 | 338 | def charm_dir(): | ||
356 | 339 | return os.environ.get('CHARM_DIR') | ||
357 | 0 | 340 | ||
358 | === added file 'hooks/charmhelpers/core/host.py' | |||
359 | --- hooks/charmhelpers/core/host.py 1970-01-01 00:00:00 +0000 | |||
360 | +++ hooks/charmhelpers/core/host.py 2013-07-16 12:39:27 +0000 | |||
361 | @@ -0,0 +1,272 @@ | |||
362 | 1 | """Tools for working with the host system""" | ||
363 | 2 | # Copyright 2012 Canonical Ltd. | ||
364 | 3 | # | ||
365 | 4 | # Authors: | ||
366 | 5 | # Nick Moffitt <nick.moffitt@canonical.com> | ||
367 | 6 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
368 | 7 | |||
369 | 8 | import apt_pkg | ||
370 | 9 | import os | ||
371 | 10 | import pwd | ||
372 | 11 | import grp | ||
373 | 12 | import subprocess | ||
374 | 13 | import hashlib | ||
375 | 14 | |||
376 | 15 | from collections import OrderedDict | ||
377 | 16 | |||
378 | 17 | from hookenv import log, execution_environment | ||
379 | 18 | |||
380 | 19 | |||
381 | 20 | def service_start(service_name): | ||
382 | 21 | service('start', service_name) | ||
383 | 22 | |||
384 | 23 | |||
385 | 24 | def service_stop(service_name): | ||
386 | 25 | service('stop', service_name) | ||
387 | 26 | |||
388 | 27 | |||
389 | 28 | def service_restart(service_name): | ||
390 | 29 | service('restart', service_name) | ||
391 | 30 | |||
392 | 31 | |||
393 | 32 | def service_reload(service_name, restart_on_failure=False): | ||
394 | 33 | if not service('reload', service_name) and restart_on_failure: | ||
395 | 34 | service('restart', service_name) | ||
396 | 35 | |||
397 | 36 | |||
398 | 37 | def service(action, service_name): | ||
399 | 38 | cmd = ['service', service_name, action] | ||
400 | 39 | return subprocess.call(cmd) == 0 | ||
401 | 40 | |||
402 | 41 | |||
403 | 42 | def service_running(service): | ||
404 | 43 | try: | ||
405 | 44 | output = subprocess.check_output(['service', service, 'status']) | ||
406 | 45 | except subprocess.CalledProcessError: | ||
407 | 46 | return False | ||
408 | 47 | else: | ||
409 | 48 | if ("start/running" in output or "is running" in output): | ||
410 | 49 | return True | ||
411 | 50 | else: | ||
412 | 51 | return False | ||
413 | 52 | |||
414 | 53 | |||
415 | 54 | def adduser(username, password=None, shell='/bin/bash', system_user=False): | ||
416 | 55 | """Add a user""" | ||
417 | 56 | try: | ||
418 | 57 | user_info = pwd.getpwnam(username) | ||
419 | 58 | log('user {0} already exists!'.format(username)) | ||
420 | 59 | except KeyError: | ||
421 | 60 | log('creating user {0}'.format(username)) | ||
422 | 61 | cmd = ['useradd'] | ||
423 | 62 | if system_user or password is None: | ||
424 | 63 | cmd.append('--system') | ||
425 | 64 | else: | ||
426 | 65 | cmd.extend([ | ||
427 | 66 | '--create-home', | ||
428 | 67 | '--shell', shell, | ||
429 | 68 | '--password', password, | ||
430 | 69 | ]) | ||
431 | 70 | cmd.append(username) | ||
432 | 71 | subprocess.check_call(cmd) | ||
433 | 72 | user_info = pwd.getpwnam(username) | ||
434 | 73 | return user_info | ||
435 | 74 | |||
436 | 75 | |||
437 | 76 | def add_user_to_group(username, group): | ||
438 | 77 | """Add a user to a group""" | ||
439 | 78 | cmd = [ | ||
440 | 79 | 'gpasswd', '-a', | ||
441 | 80 | username, | ||
442 | 81 | group | ||
443 | 82 | ] | ||
444 | 83 | log("Adding user {} to group {}".format(username, group)) | ||
445 | 84 | subprocess.check_call(cmd) | ||
446 | 85 | |||
447 | 86 | |||
448 | 87 | def rsync(from_path, to_path, flags='-r', options=None): | ||
449 | 88 | """Replicate the contents of a path""" | ||
450 | 89 | context = execution_environment() | ||
451 | 90 | options = options or ['--delete', '--executability'] | ||
452 | 91 | cmd = ['/usr/bin/rsync', flags] | ||
453 | 92 | cmd.extend(options) | ||
454 | 93 | cmd.append(from_path.format(**context)) | ||
455 | 94 | cmd.append(to_path.format(**context)) | ||
456 | 95 | log(" ".join(cmd)) | ||
457 | 96 | return subprocess.check_output(cmd).strip() | ||
458 | 97 | |||
459 | 98 | |||
460 | 99 | def symlink(source, destination): | ||
461 | 100 | """Create a symbolic link""" | ||
462 | 101 | context = execution_environment() | ||
463 | 102 | log("Symlinking {} as {}".format(source, destination)) | ||
464 | 103 | cmd = [ | ||
465 | 104 | 'ln', | ||
466 | 105 | '-sf', | ||
467 | 106 | source.format(**context), | ||
468 | 107 | destination.format(**context) | ||
469 | 108 | ] | ||
470 | 109 | subprocess.check_call(cmd) | ||
471 | 110 | |||
472 | 111 | |||
473 | 112 | def mkdir(path, owner='root', group='root', perms=0555, force=False): | ||
474 | 113 | """Create a directory""" | ||
475 | 114 | context = execution_environment() | ||
476 | 115 | log("Making dir {} {}:{} {:o}".format(path, owner, group, | ||
477 | 116 | perms)) | ||
478 | 117 | uid = pwd.getpwnam(owner.format(**context)).pw_uid | ||
479 | 118 | gid = grp.getgrnam(group.format(**context)).gr_gid | ||
480 | 119 | realpath = os.path.abspath(path) | ||
481 | 120 | if os.path.exists(realpath): | ||
482 | 121 | if force and not os.path.isdir(realpath): | ||
483 | 122 | log("Removing non-directory file {} prior to mkdir()".format(path)) | ||
484 | 123 | os.unlink(realpath) | ||
485 | 124 | else: | ||
486 | 125 | os.makedirs(realpath, perms) | ||
487 | 126 | os.chown(realpath, uid, gid) | ||
488 | 127 | |||
489 | 128 | |||
490 | 129 | def write_file(path, content, owner='root', group='root', perms=0444): | ||
491 | 130 | """Create or overwrite a file with the contents of a string""" | ||
492 | 131 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) | ||
493 | 132 | uid = pwd.getpwnam(owner).pw_uid | ||
494 | 133 | gid = grp.getgrnam(group).gr_gid | ||
495 | 134 | with open(path, 'w') as target: | ||
496 | 135 | os.fchown(target.fileno(), uid, gid) | ||
497 | 136 | os.fchmod(target.fileno(), perms) | ||
498 | 137 | target.write(content) | ||
499 | 138 | |||
500 | 139 | |||
501 | 140 | def filter_installed_packages(packages): | ||
502 | 141 | """Returns a list of packages that require installation""" | ||
503 | 142 | apt_pkg.init() | ||
504 | 143 | cache = apt_pkg.Cache() | ||
505 | 144 | _pkgs = [] | ||
506 | 145 | for package in packages: | ||
507 | 146 | try: | ||
508 | 147 | p = cache[package] | ||
509 | 148 | p.current_ver or _pkgs.append(package) | ||
510 | 149 | except KeyError: | ||
511 | 150 | log('Package {} has no installation candidate.'.format(package), | ||
512 | 151 | level='WARNING') | ||
513 | 152 | _pkgs.append(package) | ||
514 | 153 | return _pkgs | ||
515 | 154 | |||
516 | 155 | |||
517 | 156 | def apt_install(packages, options=None, fatal=False): | ||
518 | 157 | """Install one or more packages""" | ||
519 | 158 | options = options or [] | ||
520 | 159 | cmd = ['apt-get', '-y'] | ||
521 | 160 | cmd.extend(options) | ||
522 | 161 | cmd.append('install') | ||
523 | 162 | if isinstance(packages, basestring): | ||
524 | 163 | cmd.append(packages) | ||
525 | 164 | else: | ||
526 | 165 | cmd.extend(packages) | ||
527 | 166 | log("Installing {} with options: {}".format(packages, | ||
528 | 167 | options)) | ||
529 | 168 | if fatal: | ||
530 | 169 | subprocess.check_call(cmd) | ||
531 | 170 | else: | ||
532 | 171 | subprocess.call(cmd) | ||
533 | 172 | |||
534 | 173 | |||
535 | 174 | def apt_update(fatal=False): | ||
536 | 175 | """Update local apt cache""" | ||
537 | 176 | cmd = ['apt-get', 'update'] | ||
538 | 177 | if fatal: | ||
539 | 178 | subprocess.check_call(cmd) | ||
540 | 179 | else: | ||
541 | 180 | subprocess.call(cmd) | ||
542 | 181 | |||
543 | 182 | |||
544 | 183 | def mount(device, mountpoint, options=None, persist=False): | ||
545 | 184 | '''Mount a filesystem''' | ||
546 | 185 | cmd_args = ['mount'] | ||
547 | 186 | if options is not None: | ||
548 | 187 | cmd_args.extend(['-o', options]) | ||
549 | 188 | cmd_args.extend([device, mountpoint]) | ||
550 | 189 | try: | ||
551 | 190 | subprocess.check_output(cmd_args) | ||
552 | 191 | except subprocess.CalledProcessError, e: | ||
553 | 192 | log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output)) | ||
554 | 193 | return False | ||
555 | 194 | if persist: | ||
556 | 195 | # TODO: update fstab | ||
557 | 196 | pass | ||
558 | 197 | return True | ||
559 | 198 | |||
560 | 199 | |||
561 | 200 | def umount(mountpoint, persist=False): | ||
562 | 201 | '''Unmount a filesystem''' | ||
563 | 202 | cmd_args = ['umount', mountpoint] | ||
564 | 203 | try: | ||
565 | 204 | subprocess.check_output(cmd_args) | ||
566 | 205 | except subprocess.CalledProcessError, e: | ||
567 | 206 | log('Error unmounting {}\n{}'.format(mountpoint, e.output)) | ||
568 | 207 | return False | ||
569 | 208 | if persist: | ||
570 | 209 | # TODO: update fstab | ||
571 | 210 | pass | ||
572 | 211 | return True | ||
573 | 212 | |||
574 | 213 | |||
575 | 214 | def mounts(): | ||
576 | 215 | '''List of all mounted volumes as [[mountpoint,device],[...]]''' | ||
577 | 216 | with open('/proc/mounts') as f: | ||
578 | 217 | # [['/mount/point','/dev/path'],[...]] | ||
579 | 218 | system_mounts = [m[1::-1] for m in [l.strip().split() | ||
580 | 219 | for l in f.readlines()]] | ||
581 | 220 | return system_mounts | ||
582 | 221 | |||
583 | 222 | |||
584 | 223 | def file_hash(path): | ||
585 | 224 | ''' Generate a md5 hash of the contents of 'path' or None if not found ''' | ||
586 | 225 | if os.path.exists(path): | ||
587 | 226 | h = hashlib.md5() | ||
588 | 227 | with open(path, 'r') as source: | ||
589 | 228 | h.update(source.read()) # IGNORE:E1101 - it does have update | ||
590 | 229 | return h.hexdigest() | ||
591 | 230 | else: | ||
592 | 231 | return None | ||
593 | 232 | |||
594 | 233 | |||
595 | 234 | def restart_on_change(restart_map): | ||
596 | 235 | ''' Restart services based on configuration files changing | ||
597 | 236 | |||
598 | 237 | This function is used a decorator, for example | ||
599 | 238 | |||
600 | 239 | @restart_on_change({ | ||
601 | 240 | '/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ] | ||
602 | 241 | }) | ||
603 | 242 | def ceph_client_changed(): | ||
604 | 243 | ... | ||
605 | 244 | |||
606 | 245 | In this example, the cinder-api and cinder-volume services | ||
607 | 246 | would be restarted if /etc/ceph/ceph.conf is changed by the | ||
608 | 247 | ceph_client_changed function. | ||
609 | 248 | ''' | ||
610 | 249 | def wrap(f): | ||
611 | 250 | def wrapped_f(*args): | ||
612 | 251 | checksums = {} | ||
613 | 252 | for path in restart_map: | ||
614 | 253 | checksums[path] = file_hash(path) | ||
615 | 254 | f(*args) | ||
616 | 255 | restarts = [] | ||
617 | 256 | for path in restart_map: | ||
618 | 257 | if checksums[path] != file_hash(path): | ||
619 | 258 | restarts += restart_map[path] | ||
620 | 259 | for service_name in list(OrderedDict.fromkeys(restarts)): | ||
621 | 260 | service('restart', service_name) | ||
622 | 261 | return wrapped_f | ||
623 | 262 | return wrap | ||
624 | 263 | |||
625 | 264 | |||
626 | 265 | def lsb_release(): | ||
627 | 266 | '''Return /etc/lsb-release in a dict''' | ||
628 | 267 | d = {} | ||
629 | 268 | with open('/etc/lsb-release', 'r') as lsb: | ||
630 | 269 | for l in lsb: | ||
631 | 270 | k, v = l.split('=') | ||
632 | 271 | d[k.strip()] = v.strip() | ||
633 | 272 | return d | ||
634 | 0 | 273 | ||
635 | === modified file 'hooks/hooks.py' | |||
636 | --- hooks/hooks.py 2013-05-28 09:53:30 +0000 | |||
637 | +++ hooks/hooks.py 2013-07-16 12:39:27 +0000 | |||
638 | @@ -1,89 +1,52 @@ | |||
639 | 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
640 | 2 | 2 | ||
641 | 3 | from grp import getgrnam | ||
642 | 4 | import json | ||
643 | 5 | import os.path | 3 | import os.path |
644 | 6 | from pwd import getpwnam | ||
645 | 7 | import shutil | 4 | import shutil |
647 | 8 | import subprocess | 5 | import sys |
648 | 9 | from textwrap import dedent | 6 | from textwrap import dedent |
649 | 10 | 7 | ||
650 | 8 | from charmhelpers.core import hookenv, host | ||
651 | 9 | from charmhelpers.core.hookenv import log, DEBUG, INFO | ||
652 | 10 | |||
653 | 11 | 11 | ||
654 | 12 | CLIENT_RELATION_TYPES = frozenset(['db', 'db-admin']) | 12 | CLIENT_RELATION_TYPES = frozenset(['db', 'db-admin']) |
655 | 13 | 13 | ||
656 | 14 | 14 | ||
657 | 15 | def relation_ids(relation_types): | ||
658 | 16 | relids = [] | ||
659 | 17 | for reltype in relation_types: | ||
660 | 18 | relid_cmd_line = ['relation-ids', '--format=json', reltype] | ||
661 | 19 | json_relids = subprocess.check_output(relid_cmd_line).strip() | ||
662 | 20 | if json_relids: | ||
663 | 21 | relids.extend(json.loads(json_relids)) | ||
664 | 22 | return relids | ||
665 | 23 | |||
666 | 24 | |||
667 | 25 | def relation_list(relation_id): | ||
668 | 26 | """Return the list of units participating in the relation.""" | ||
669 | 27 | cmd = ['relation-list', '--format=json', '-r', relation_id] | ||
670 | 28 | json_units = subprocess.check_output(cmd).strip() | ||
671 | 29 | if json_units: | ||
672 | 30 | return json.loads(json_units) | ||
673 | 31 | return [] | ||
674 | 32 | |||
675 | 33 | |||
676 | 34 | def relation_get(relation_id, unit): | ||
677 | 35 | cmd = ['relation-get', '--format=json', '-r', relation_id, '-', unit] | ||
678 | 36 | json_relation = subprocess.check_output(cmd) | ||
679 | 37 | if json_relation: | ||
680 | 38 | return json.loads(json_relation) | ||
681 | 39 | return {} | ||
682 | 40 | |||
683 | 41 | |||
684 | 42 | def relation_set(relation_id, keyvalues): | ||
685 | 43 | command = ['relation-set', '-r', relation_id] | ||
686 | 44 | command.extend( | ||
687 | 45 | ["{}={}".format(k, v or '') for k, v in keyvalues.items()]) | ||
688 | 46 | subprocess.check_call(command) | ||
689 | 47 | |||
690 | 48 | |||
691 | 49 | def config_get(): | ||
692 | 50 | command = ['config-get', '--format=json'] | ||
693 | 51 | json_config = subprocess.check_output(command) | ||
694 | 52 | if json_config: | ||
695 | 53 | return json.loads(json_config) | ||
696 | 54 | return {} | ||
697 | 55 | |||
698 | 56 | |||
699 | 57 | def all_relations(relation_types=CLIENT_RELATION_TYPES): | 15 | def all_relations(relation_types=CLIENT_RELATION_TYPES): |
700 | 58 | for reltype in relation_types: | 16 | for reltype in relation_types: |
706 | 59 | for relid in relation_ids(relation_types=[reltype]): | 17 | for relid in hookenv.relation_ids(reltype): |
707 | 60 | for unit in relation_list(relation_id=relid): | 18 | for unit in hookenv.related_units(relid): |
708 | 61 | yield reltype, relid, unit, relation_get(relid, unit) | 19 | yield reltype, relid, unit, hookenv.relation_get( |
709 | 62 | 20 | unit=unit, rid=relid) | |
710 | 63 | 21 | ||
711 | 22 | hooks = hookenv.Hooks() | ||
712 | 23 | |||
713 | 24 | @hooks.hook( | ||
714 | 25 | 'config-changed', 'upgrade-charm', | ||
715 | 26 | 'db-relation-changed', 'db-relation-joined', | ||
716 | 27 | 'db-admin-relation-changed', 'db-admin-relation-joined') | ||
717 | 64 | def rebuild_all_relations(): | 28 | def rebuild_all_relations(): |
719 | 65 | config = config_get() | 29 | config = hookenv.config() |
720 | 66 | 30 | ||
721 | 67 | # Clear out old scripts and pgpass files | 31 | # Clear out old scripts and pgpass files |
722 | 68 | if os.path.exists('bin'): | 32 | if os.path.exists('bin'): |
723 | 69 | shutil.rmtree('bin') | 33 | shutil.rmtree('bin') |
724 | 70 | if os.path.exists('pgpass'): | 34 | if os.path.exists('pgpass'): |
725 | 71 | shutil.rmtree('pgpass') | 35 | shutil.rmtree('pgpass') |
728 | 72 | install_dir('bin', mode=0o755) | 36 | host.mkdir('bin', perms=0o755) |
729 | 73 | install_dir('pgpass', group='ubuntu', mode=0o750) | 37 | host.mkdir('pgpass', group='ubuntu', perms=0o750) |
730 | 74 | 38 | ||
731 | 75 | for _, relid, unit, relation in all_relations(relation_types=['db']): | 39 | for _, relid, unit, relation in all_relations(relation_types=['db']): |
733 | 76 | juju_log(MSG_DEBUG, "{} {} {!r}".format(relid, unit, relation)) | 40 | log("{} {} {!r}".format(relid, unit, relation), DEBUG) |
734 | 77 | if config['database'] and relation['database'] != config['database']: | 41 | if config['database'] and relation['database'] != config['database']: |
739 | 78 | juju_log( | 42 | log("Overriding generated database {} with {}".format( |
740 | 79 | MSG_INFO, "Overriding generated database {} with {}".format( | 43 | relation['database'], config['database']), INFO) |
741 | 80 | relation['database'], config['database'])) | 44 | hookenv.relation_set(relid, database=config['database']) |
738 | 81 | relation_set(relid, dict(database=config['database'])) | ||
742 | 82 | elif 'user' in relation: | 45 | elif 'user' in relation: |
743 | 83 | rebuild_relation(relid, unit, relation) | 46 | rebuild_relation(relid, unit, relation) |
744 | 84 | 47 | ||
745 | 85 | for _, relid, unit, relation in all_relations(relation_types=['db-admin']): | 48 | for _, relid, unit, relation in all_relations(relation_types=['db-admin']): |
747 | 86 | juju_log(MSG_DEBUG, "{} {} {!r}".format(relid, unit, relation)) | 49 | log("{} {} {!r}".format(relid, unit, relation), DEBUG) |
748 | 87 | if 'user' in relation: | 50 | if 'user' in relation: |
749 | 88 | rebuild_relation(relid, unit, relation) | 51 | rebuild_relation(relid, unit, relation) |
750 | 89 | 52 | ||
751 | @@ -91,11 +54,17 @@ | |||
752 | 91 | def rebuild_relation(relid, unit, relation): | 54 | def rebuild_relation(relid, unit, relation): |
753 | 92 | relname = relid.split(':')[0] | 55 | relname = relid.split(':')[0] |
754 | 93 | unitname = unit.replace('/', '-') | 56 | unitname = unit.replace('/', '-') |
755 | 57 | this_unit = hookenv.local_unit() | ||
756 | 58 | |||
757 | 59 | allowed_units = relation.get('allowed-units', '') | ||
758 | 60 | if this_unit not in allowed_units.split(): | ||
759 | 61 | log("Not yet authorized on {}".format(relid), INFO) | ||
760 | 62 | return | ||
761 | 94 | 63 | ||
762 | 95 | script_name = 'psql-{}-{}'.format(relname, unitname) | 64 | script_name = 'psql-{}-{}'.format(relname, unitname) |
763 | 96 | build_script(script_name, relation) | 65 | build_script(script_name, relation) |
764 | 97 | state = relation.get('state', None) | 66 | state = relation.get('state', None) |
766 | 98 | if state and state != 'standalone': | 67 | if state in ('master', 'hot standby'): |
767 | 99 | script_name = 'psql-{}-{}'.format(relname, state.replace(' ', '-')) | 68 | script_name = 'psql-{}-{}'.format(relname, state.replace(' ', '-')) |
768 | 100 | build_script(script_name, relation) | 69 | build_script(script_name, relation) |
769 | 101 | 70 | ||
770 | @@ -117,45 +86,18 @@ | |||
771 | 117 | database=relation.get('database', ''), # db-admin has no database | 86 | database=relation.get('database', ''), # db-admin has no database |
772 | 118 | user=relation['user'], | 87 | user=relation['user'], |
773 | 119 | pgpass=pgpass_path) | 88 | pgpass=pgpass_path) |
777 | 120 | juju_log(MSG_INFO, "Generating wrapper {}".format(script_path)) | 89 | log("Generating wrapper {}".format(script_path), INFO) |
778 | 121 | install_file( | 90 | host.write_file( |
779 | 122 | script, script_path, owner="ubuntu", group="ubuntu", mode=0o700) | 91 | script_path, script, owner="ubuntu", group="ubuntu", perms=0o700) |
780 | 123 | 92 | ||
781 | 124 | # The wrapper requires access to the password, stored in a .pgpass | 93 | # The wrapper requires access to the password, stored in a .pgpass |
782 | 125 | # file so it isn't exposed in an environment variable or on the | 94 | # file so it isn't exposed in an environment variable or on the |
783 | 126 | # command line. | 95 | # command line. |
784 | 127 | pgpass = "*:*:*:{user}:{password}".format( | 96 | pgpass = "*:*:*:{user}:{password}".format( |
785 | 128 | user=relation['user'], password=relation['password']) | 97 | user=relation['user'], password=relation['password']) |
815 | 129 | install_file( | 98 | host.write_file( |
816 | 130 | pgpass, pgpass_path, owner="ubuntu", group="ubuntu", mode=0o400) | 99 | pgpass_path, pgpass, owner="ubuntu", group="ubuntu", mode=0o400) |
788 | 131 | |||
789 | 132 | |||
790 | 133 | def install_file(contents, dest, owner="root", group="root", mode=0o600): | ||
791 | 134 | uid = getpwnam(owner)[2] | ||
792 | 135 | gid = getgrnam(group)[2] | ||
793 | 136 | dest_fd = os.open(dest, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode) | ||
794 | 137 | os.fchown(dest_fd, uid, gid) | ||
795 | 138 | with os.fdopen(dest_fd, 'w') as destfile: | ||
796 | 139 | destfile.write(str(contents)) | ||
797 | 140 | |||
798 | 141 | |||
799 | 142 | def install_dir(dirname, owner="root", group="root", mode=0o700): | ||
800 | 143 | command = [ | ||
801 | 144 | '/usr/bin/install', | ||
802 | 145 | '-o', owner, '-g', group, '-m', oct(mode), '-d', dirname] | ||
803 | 146 | subprocess.check_call(command) | ||
804 | 147 | |||
805 | 148 | |||
806 | 149 | MSG_CRITICAL = "CRITICAL" | ||
807 | 150 | MSG_DEBUG = "DEBUG" | ||
808 | 151 | MSG_INFO = "INFO" | ||
809 | 152 | MSG_ERROR = "ERROR" | ||
810 | 153 | MSG_WARNING = "WARNING" | ||
811 | 154 | |||
812 | 155 | |||
813 | 156 | def juju_log(level, msg): | ||
814 | 157 | subprocess.call(['juju-log', '-l', level, msg]) | ||
817 | 158 | 100 | ||
818 | 159 | 101 | ||
819 | 160 | if __name__ == '__main__': | 102 | if __name__ == '__main__': |
821 | 161 | raise SystemExit(rebuild_all_relations()) | 103 | hooks.execute(sys.argv) |
822 | 162 | 104 | ||
823 | === modified file 'icon.svg' | |||
824 | --- icon.svg 2013-06-04 11:32:34 +0000 | |||
825 | +++ icon.svg 2013-07-16 12:39:27 +0000 | |||
826 | @@ -1,4 +1,6 @@ | |||
827 | 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
828 | 2 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||
829 | 3 | |||
830 | 2 | <svg | 4 | <svg |
831 | 3 | xmlns:dc="http://purl.org/dc/elements/1.1/" | 5 | xmlns:dc="http://purl.org/dc/elements/1.1/" |
832 | 4 | xmlns:cc="http://creativecommons.org/ns#" | 6 | xmlns:cc="http://creativecommons.org/ns#" |
833 | @@ -7,86 +9,350 @@ | |||
834 | 7 | xmlns="http://www.w3.org/2000/svg" | 9 | xmlns="http://www.w3.org/2000/svg" |
835 | 8 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | 10 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
836 | 9 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | 11 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
842 | 10 | width="432.071pt" | 12 | width="96" |
843 | 11 | height="445.383pt" | 13 | height="96" |
844 | 12 | viewBox="0 0 432.071 445.383" | 14 | id="svg6517" |
840 | 13 | xml:space="preserve" | ||
841 | 14 | id="svg2" | ||
845 | 15 | version="1.1" | 15 | version="1.1" |
846 | 16 | inkscape:version="0.48.4 r9939" | 16 | inkscape:version="0.48.4 r9939" |
852 | 17 | sodipodi:docname="icon.svg"><metadata | 17 | sodipodi:docname="icon.svg"> |
853 | 18 | id="metadata34"><rdf:RDF><cc:Work | 18 | <defs |
854 | 19 | rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type | 19 | id="defs6519"> |
855 | 20 | rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs | 20 | <linearGradient |
856 | 21 | id="defs32" /><sodipodi:namedview | 21 | id="linearGradient3947"> |
857 | 22 | <stop | ||
858 | 23 | style="stop-color:#ffffff;stop-opacity:1;" | ||
859 | 24 | offset="0" | ||
860 | 25 | id="stop3949" /> | ||
861 | 26 | <stop | ||
862 | 27 | style="stop-color:#ffffff;stop-opacity:0;" | ||
863 | 28 | offset="1" | ||
864 | 29 | id="stop3951" /> | ||
865 | 30 | </linearGradient> | ||
866 | 31 | <linearGradient | ||
867 | 32 | id="Background"> | ||
868 | 33 | <stop | ||
869 | 34 | id="stop4178" | ||
870 | 35 | offset="0" | ||
871 | 36 | style="stop-color:#b8b8b8;stop-opacity:1" /> | ||
872 | 37 | <stop | ||
873 | 38 | id="stop4180" | ||
874 | 39 | offset="1" | ||
875 | 40 | style="stop-color:#c9c9c9;stop-opacity:1" /> | ||
876 | 41 | </linearGradient> | ||
877 | 42 | <filter | ||
878 | 43 | style="color-interpolation-filters:sRGB;" | ||
879 | 44 | inkscape:label="Inner Shadow" | ||
880 | 45 | id="filter1121"> | ||
881 | 46 | <feFlood | ||
882 | 47 | flood-opacity="0.59999999999999998" | ||
883 | 48 | flood-color="rgb(0,0,0)" | ||
884 | 49 | result="flood" | ||
885 | 50 | id="feFlood1123" /> | ||
886 | 51 | <feComposite | ||
887 | 52 | in="flood" | ||
888 | 53 | in2="SourceGraphic" | ||
889 | 54 | operator="out" | ||
890 | 55 | result="composite1" | ||
891 | 56 | id="feComposite1125" /> | ||
892 | 57 | <feGaussianBlur | ||
893 | 58 | in="composite1" | ||
894 | 59 | stdDeviation="1" | ||
895 | 60 | result="blur" | ||
896 | 61 | id="feGaussianBlur1127" /> | ||
897 | 62 | <feOffset | ||
898 | 63 | dx="0" | ||
899 | 64 | dy="2" | ||
900 | 65 | result="offset" | ||
901 | 66 | id="feOffset1129" /> | ||
902 | 67 | <feComposite | ||
903 | 68 | in="offset" | ||
904 | 69 | in2="SourceGraphic" | ||
905 | 70 | operator="atop" | ||
906 | 71 | result="composite2" | ||
907 | 72 | id="feComposite1131" /> | ||
908 | 73 | </filter> | ||
909 | 74 | <filter | ||
910 | 75 | style="color-interpolation-filters:sRGB;" | ||
911 | 76 | inkscape:label="Drop Shadow" | ||
912 | 77 | id="filter950"> | ||
913 | 78 | <feFlood | ||
914 | 79 | flood-opacity="0.25" | ||
915 | 80 | flood-color="rgb(0,0,0)" | ||
916 | 81 | result="flood" | ||
917 | 82 | id="feFlood952" /> | ||
918 | 83 | <feComposite | ||
919 | 84 | in="flood" | ||
920 | 85 | in2="SourceGraphic" | ||
921 | 86 | operator="in" | ||
922 | 87 | result="composite1" | ||
923 | 88 | id="feComposite954" /> | ||
924 | 89 | <feGaussianBlur | ||
925 | 90 | in="composite1" | ||
926 | 91 | stdDeviation="1" | ||
927 | 92 | result="blur" | ||
928 | 93 | id="feGaussianBlur956" /> | ||
929 | 94 | <feOffset | ||
930 | 95 | dx="0" | ||
931 | 96 | dy="1" | ||
932 | 97 | result="offset" | ||
933 | 98 | id="feOffset958" /> | ||
934 | 99 | <feComposite | ||
935 | 100 | in="SourceGraphic" | ||
936 | 101 | in2="offset" | ||
937 | 102 | operator="over" | ||
938 | 103 | result="composite2" | ||
939 | 104 | id="feComposite960" /> | ||
940 | 105 | </filter> | ||
941 | 106 | <clipPath | ||
942 | 107 | clipPathUnits="userSpaceOnUse" | ||
943 | 108 | id="clipPath873"> | ||
944 | 109 | <g | ||
945 | 110 | transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)" | ||
946 | 111 | id="g875" | ||
947 | 112 | inkscape:label="Layer 1" | ||
948 | 113 | style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"> | ||
949 | 114 | <path | ||
950 | 115 | style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" | ||
951 | 116 | d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z" | ||
952 | 117 | id="path877" | ||
953 | 118 | inkscape:connector-curvature="0" | ||
954 | 119 | sodipodi:nodetypes="sssssssss" /> | ||
955 | 120 | </g> | ||
956 | 121 | </clipPath> | ||
957 | 122 | <filter | ||
958 | 123 | inkscape:collect="always" | ||
959 | 124 | id="filter891" | ||
960 | 125 | inkscape:label="Badge Shadow"> | ||
961 | 126 | <feGaussianBlur | ||
962 | 127 | inkscape:collect="always" | ||
963 | 128 | stdDeviation="0.71999962" | ||
964 | 129 | id="feGaussianBlur893" /> | ||
965 | 130 | </filter> | ||
966 | 131 | </defs> | ||
967 | 132 | <sodipodi:namedview | ||
968 | 133 | id="base" | ||
969 | 22 | pagecolor="#ffffff" | 134 | pagecolor="#ffffff" |
970 | 23 | bordercolor="#666666" | 135 | bordercolor="#666666" |
976 | 24 | borderopacity="1" | 136 | borderopacity="1.0" |
977 | 25 | objecttolerance="10" | 137 | inkscape:pageopacity="0.0" |
973 | 26 | gridtolerance="10" | ||
974 | 27 | guidetolerance="10" | ||
975 | 28 | inkscape:pageopacity="0" | ||
978 | 29 | inkscape:pageshadow="2" | 138 | inkscape:pageshadow="2" |
988 | 30 | inkscape:window-width="1111" | 139 | inkscape:zoom="4.0745362" |
989 | 31 | inkscape:window-height="783" | 140 | inkscape:cx="49.256842" |
990 | 32 | id="namedview30" | 141 | inkscape:cy="43.407288" |
991 | 33 | showgrid="false" | 142 | inkscape:document-units="px" |
992 | 34 | inkscape:zoom="0.42390481" | 143 | inkscape:current-layer="layer3" |
993 | 35 | inkscape:cx="270.04437" | 144 | showgrid="true" |
994 | 36 | inkscape:cy="278.36438" | 145 | fit-margin-top="0" |
995 | 37 | inkscape:window-x="195" | 146 | fit-margin-left="0" |
996 | 38 | inkscape:window-y="148" | 147 | fit-margin-right="0" |
997 | 148 | fit-margin-bottom="0" | ||
998 | 149 | inkscape:window-width="1417" | ||
999 | 150 | inkscape:window-height="799" | ||
1000 | 151 | inkscape:window-x="65" | ||
1001 | 152 | inkscape:window-y="24" | ||
1002 | 39 | inkscape:window-maximized="0" | 153 | inkscape:window-maximized="0" |
1003 | 40 | inkscape:current-layer="svg2" /><g | ||
1004 | 41 | id="orginal" | ||
1005 | 42 | style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;" /><g | ||
1006 | 43 | id="Layer_x0020_3" | ||
1007 | 44 | style="fill-rule:nonzero;clip-rule:nonzero;fill:none;stroke:#FFFFFF;stroke-width:12.4651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;"><path | ||
1008 | 45 | style="fill:#000000;stroke:#000000;stroke-width:37.3953;stroke-linecap:butt;stroke-linejoin:miter;" | ||
1009 | 46 | d="M323.205,324.227c2.833-23.601,1.984-27.062,19.563-23.239l4.463,0.392c13.517,0.615,31.199-2.174,41.587-7c22.362-10.376,35.622-27.7,13.572-23.148c-50.297,10.376-53.755-6.655-53.755-6.655c53.111-78.803,75.313-178.836,56.149-203.322 C352.514-5.534,262.036,26.049,260.522,26.869l-0.482,0.089c-9.938-2.062-21.06-3.294-33.554-3.496c-22.761-0.374-40.032,5.967-53.133,15.904c0,0-161.408-66.498-153.899,83.628c1.597,31.936,45.777,241.655,98.47,178.31 c19.259-23.163,37.871-42.748,37.871-42.748c9.242,6.14,20.307,9.272,31.912,8.147l0.897-0.765c-0.281,2.876-0.157,5.689,0.359,9.019c-13.572,15.167-9.584,17.83-36.723,23.416c-27.457,5.659-11.326,15.734-0.797,18.367c12.768,3.193,42.305,7.716,62.268-20.224 l-0.795,3.188c5.325,4.26,4.965,30.619,5.72,49.452c0.756,18.834,2.017,36.409,5.856,46.771c3.839,10.36,8.369,37.05,44.036,29.406c29.809-6.388,52.6-15.582,54.677-101.107" | ||
1010 | 47 | id="path6" /><path | ||
1011 | 48 | style="fill:#336791;stroke:none;" | ||
1012 | 49 | d="M402.395,271.23c-50.302,10.376-53.76-6.655-53.76-6.655c53.111-78.808,75.313-178.843,56.153-203.326c-52.27-66.785-142.752-35.2-144.262-34.38l-0.486,0.087c-9.938-2.063-21.06-3.292-33.56-3.496c-22.761-0.373-40.026,5.967-53.127,15.902 c0,0-161.411-66.495-153.904,83.63c1.597,31.938,45.776,241.657,98.471,178.312c19.26-23.163,37.869-42.748,37.869-42.748c9.243,6.14,20.308,9.272,31.908,8.147l0.901-0.765c-0.28,2.876-0.152,5.689,0.361,9.019c-13.575,15.167-9.586,17.83-36.723,23.416 c-27.459,5.659-11.328,15.734-0.796,18.367c12.768,3.193,42.307,7.716,62.266-20.224l-0.796,3.188c5.319,4.26,9.054,27.711,8.428,48.969c-0.626,21.259-1.044,35.854,3.147,47.254c4.191,11.4,8.368,37.05,44.042,29.406c29.809-6.388,45.256-22.942,47.405-50.555 c1.525-19.631,4.976-16.729,5.194-34.28l2.768-8.309c3.192-26.611,0.507-35.196,18.872-31.203l4.463,0.392c13.517,0.615,31.208-2.174,41.591-7c22.358-10.376,35.618-27.7,13.573-23.148z" | ||
1013 | 50 | id="path8" /><path | ||
1014 | 51 | d="M215.866,286.484c-1.385,49.516,0.348,99.377,5.193,111.495c4.848,12.118,15.223,35.688,50.9,28.045c29.806-6.39,40.651-18.756,45.357-46.051c3.466-20.082,10.148-75.854,11.005-87.281" | ||
1015 | 52 | id="path10" /><path | ||
1016 | 53 | d="M173.104,38.256c0,0-161.521-66.016-154.012,84.109c1.597,31.938,45.779,241.664,98.473,178.316c19.256-23.166,36.671-41.335,36.671-41.335" | ||
1017 | 54 | id="path12" /><path | ||
1018 | 55 | d="M260.349,26.207c-5.591,1.753,89.848-34.889,144.087,34.417c19.159,24.484-3.043,124.519-56.153,203.329" | ||
1019 | 56 | id="path14" /><path | ||
1020 | 57 | style="stroke-linejoin:bevel;" | ||
1021 | 58 | d="M348.282,263.953c0,0,3.461,17.036,53.764,6.653c22.04-4.552,8.776,12.774-13.577,23.155c-18.345,8.514-59.474,10.696-60.146-1.069c-1.729-30.355,21.647-21.133,19.96-28.739c-1.525-6.85-11.979-13.573-18.894-30.338 c-6.037-14.633-82.796-126.849,21.287-110.183c3.813-0.789-27.146-99.002-124.553-100.599c-97.385-1.597-94.19,119.762-94.19,119.762" | ||
1022 | 59 | id="path16" /><path | ||
1023 | 60 | d="M188.604,274.334c-13.577,15.166-9.584,17.829-36.723,23.417c-27.459,5.66-11.326,15.733-0.797,18.365c12.768,3.195,42.307,7.718,62.266-20.229c6.078-8.509-0.036-22.086-8.385-25.547c-4.034-1.671-9.428-3.765-16.361,3.994z" | ||
1024 | 61 | id="path18" /><path | ||
1025 | 62 | d="M187.715,274.069c-1.368-8.917,2.93-19.528,7.536-31.942c6.922-18.626,22.893-37.255,10.117-96.339c-9.523-44.029-73.396-9.163-73.436-3.193c-0.039,5.968,2.889,30.26-1.067,58.548c-5.162,36.913,23.488,68.132,56.479,64.938" | ||
1026 | 63 | id="path20" /><path | ||
1027 | 64 | style="fill:#FFFFFF;stroke-width:4.155;stroke-linecap:butt;stroke-linejoin:miter;" | ||
1028 | 65 | d="M172.517,141.7c-0.288,2.039,3.733,7.48,8.976,8.207c5.234,0.73,9.714-3.522,9.998-5.559c0.284-2.039-3.732-4.285-8.977-5.015c-5.237-0.731-9.719,0.333-9.996,2.367z" | ||
1029 | 66 | id="path22" /><path | ||
1030 | 67 | style="fill:#FFFFFF;stroke-width:2.0775;stroke-linecap:butt;stroke-linejoin:miter;" | ||
1031 | 68 | d="M331.941,137.543c0.284,2.039-3.732,7.48-8.976,8.207c-5.238,0.73-9.718-3.522-10.005-5.559c-0.277-2.039,3.74-4.285,8.979-5.015c5.239-0.73,9.718,0.333,10.002,2.368z" | ||
1032 | 69 | id="path24" /><path | ||
1033 | 70 | d="M350.676,123.432c0.863,15.994-3.445,26.888-3.988,43.914c-0.804,24.748,11.799,53.074-7.191,81.435" | ||
1034 | 71 | id="path26" /><path | ||
1035 | 72 | style="stroke-width:3;" | ||
1036 | 73 | d="M0,60.232" | ||
1037 | 74 | id="path28" /></g><rect | ||
1038 | 75 | style="fill:#4d4d4d;stroke:#000000;stroke-width:0.73861116" | ||
1039 | 76 | id="rect3015" | ||
1040 | 77 | width="258.81003" | ||
1041 | 78 | height="105.94549" | ||
1042 | 79 | x="-2.0179098" | ||
1043 | 80 | y="341.45541" /><text | ||
1044 | 81 | xml:space="preserve" | ||
1045 | 82 | style="font-size:68.27682495px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Courier 10 Pitch;-inkscape-font-specification:Courier 10 Pitch" | ||
1046 | 83 | x="10.404695" | ||
1047 | 84 | y="396.7569" | ||
1048 | 85 | id="text3011-6" | ||
1049 | 86 | sodipodi:linespacing="125%" | ||
1050 | 87 | transform="scale(0.96172302,1.0398004)"><tspan | ||
1051 | 88 | sodipodi:role="line" | ||
1052 | 89 | id="tspan3013-6" | ||
1053 | 90 | x="10.404695" | ||
1054 | 91 | y="396.7569">psql>_</tspan></text> | ||
1055 | 92 | </svg> | ||
1056 | 93 | \ No newline at end of file | 154 | \ No newline at end of file |
1057 | 155 | showborder="true" | ||
1058 | 156 | showguides="true" | ||
1059 | 157 | inkscape:guide-bbox="true" | ||
1060 | 158 | inkscape:showpageshadow="false"> | ||
1061 | 159 | <inkscape:grid | ||
1062 | 160 | type="xygrid" | ||
1063 | 161 | id="grid821" /> | ||
1064 | 162 | <sodipodi:guide | ||
1065 | 163 | orientation="1,0" | ||
1066 | 164 | position="16,48" | ||
1067 | 165 | id="guide823" /> | ||
1068 | 166 | <sodipodi:guide | ||
1069 | 167 | orientation="0,1" | ||
1070 | 168 | position="64,80" | ||
1071 | 169 | id="guide825" /> | ||
1072 | 170 | <sodipodi:guide | ||
1073 | 171 | orientation="1,0" | ||
1074 | 172 | position="80,40" | ||
1075 | 173 | id="guide827" /> | ||
1076 | 174 | <sodipodi:guide | ||
1077 | 175 | orientation="0,1" | ||
1078 | 176 | position="64,16" | ||
1079 | 177 | id="guide829" /> | ||
1080 | 178 | </sodipodi:namedview> | ||
1081 | 179 | <metadata | ||
1082 | 180 | id="metadata6522"> | ||
1083 | 181 | <rdf:RDF> | ||
1084 | 182 | <cc:Work | ||
1085 | 183 | rdf:about=""> | ||
1086 | 184 | <dc:format>image/svg+xml</dc:format> | ||
1087 | 185 | <dc:type | ||
1088 | 186 | rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||
1089 | 187 | <dc:title></dc:title> | ||
1090 | 188 | </cc:Work> | ||
1091 | 189 | </rdf:RDF> | ||
1092 | 190 | </metadata> | ||
1093 | 191 | <g | ||
1094 | 192 | inkscape:label="BACKGROUND" | ||
1095 | 193 | inkscape:groupmode="layer" | ||
1096 | 194 | id="layer1" | ||
1097 | 195 | transform="translate(268,-635.29076)" | ||
1098 | 196 | style="display:inline"> | ||
1099 | 197 | <path | ||
1100 | 198 | style="fill:#ffffff;fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121);opacity:1" | ||
1101 | 199 | d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z" | ||
1102 | 200 | id="path6455" | ||
1103 | 201 | inkscape:connector-curvature="0" | ||
1104 | 202 | sodipodi:nodetypes="sssssssss" /> | ||
1105 | 203 | </g> | ||
1106 | 204 | <g | ||
1107 | 205 | inkscape:groupmode="layer" | ||
1108 | 206 | id="layer3" | ||
1109 | 207 | inkscape:label="PLACE YOUR PICTOGRAM HERE" | ||
1110 | 208 | style="display:inline"> | ||
1111 | 209 | <g | ||
1112 | 210 | transform="matrix(0.18231452,0,0,0.16578318,13.59438,4.5326341)" | ||
1113 | 211 | id="Layer_x0020_3" | ||
1114 | 212 | style="fill:none;stroke:#ffffff;stroke-width:12.46510029;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;display:inline"> | ||
1115 | 213 | <path | ||
1116 | 214 | inkscape:connector-curvature="0" | ||
1117 | 215 | style="fill:#000000;stroke:#000000;stroke-width:37.39530182;stroke-linecap:butt;stroke-linejoin:miter" | ||
1118 | 216 | d="m 323.205,324.227 c 2.833,-23.601 1.984,-27.062 19.563,-23.239 l 4.463,0.392 c 13.517,0.615 31.199,-2.174 41.587,-7 22.362,-10.376 35.622,-27.7 13.572,-23.148 -50.297,10.376 -53.755,-6.655 -53.755,-6.655 C 401.746,185.774 423.948,85.741 404.784,61.255 352.514,-5.534 262.036,26.049 260.522,26.869 l -0.482,0.089 c -9.938,-2.062 -21.06,-3.294 -33.554,-3.496 -22.761,-0.374 -40.032,5.967 -53.133,15.904 0,0 -161.408,-66.498 -153.899,83.628 1.597,31.936 45.777,241.655 98.47,178.31 19.259,-23.163 37.871,-42.748 37.871,-42.748 9.242,6.14 20.307,9.272 31.912,8.147 l 0.897,-0.765 c -0.281,2.876 -0.157,5.689 0.359,9.019 -13.572,15.167 -9.584,17.83 -36.723,23.416 -27.457,5.659 -11.326,15.734 -0.797,18.367 12.768,3.193 42.305,7.716 62.268,-20.224 l -0.795,3.188 c 5.325,4.26 4.965,30.619 5.72,49.452 0.756,18.834 2.017,36.409 5.856,46.771 3.839,10.36 8.369,37.05 44.036,29.406 29.809,-6.388 52.6,-15.582 54.677,-101.107" | ||
1119 | 217 | id="path3095" /> | ||
1120 | 218 | <path | ||
1121 | 219 | inkscape:connector-curvature="0" | ||
1122 | 220 | style="fill:#336791;stroke:none" | ||
1123 | 221 | d="m 402.395,271.23 c -50.302,10.376 -53.76,-6.655 -53.76,-6.655 53.111,-78.808 75.313,-178.843 56.153,-203.326 -52.27,-66.785 -142.752,-35.2 -144.262,-34.38 l -0.486,0.087 c -9.938,-2.063 -21.06,-3.292 -33.56,-3.496 -22.761,-0.373 -40.026,5.967 -53.127,15.902 0,0 -161.411,-66.495 -153.904,83.63 1.597,31.938 45.776,241.657 98.471,178.312 19.26,-23.163 37.869,-42.748 37.869,-42.748 9.243,6.14 20.308,9.272 31.908,8.147 l 0.901,-0.765 c -0.28,2.876 -0.152,5.689 0.361,9.019 -13.575,15.167 -9.586,17.83 -36.723,23.416 -27.459,5.659 -11.328,15.734 -0.796,18.367 12.768,3.193 42.307,7.716 62.266,-20.224 l -0.796,3.188 c 5.319,4.26 9.054,27.711 8.428,48.969 -0.626,21.259 -1.044,35.854 3.147,47.254 4.191,11.4 8.368,37.05 44.042,29.406 29.809,-6.388 45.256,-22.942 47.405,-50.555 1.525,-19.631 4.976,-16.729 5.194,-34.28 l 2.768,-8.309 c 3.192,-26.611 0.507,-35.196 18.872,-31.203 l 4.463,0.392 c 13.517,0.615 31.208,-2.174 41.591,-7 22.358,-10.376 35.618,-27.7 13.573,-23.148 z" | ||
1124 | 222 | id="path3097" /> | ||
1125 | 223 | <path | ||
1126 | 224 | inkscape:connector-curvature="0" | ||
1127 | 225 | d="m 215.866,286.484 c -1.385,49.516 0.348,99.377 5.193,111.495 4.848,12.118 15.223,35.688 50.9,28.045 29.806,-6.39 40.651,-18.756 45.357,-46.051 3.466,-20.082 10.148,-75.854 11.005,-87.281" | ||
1128 | 226 | id="path3099" /> | ||
1129 | 227 | <path | ||
1130 | 228 | inkscape:connector-curvature="0" | ||
1131 | 229 | d="m 173.104,38.256 c 0,0 -161.521,-66.016 -154.012,84.109 1.597,31.938 45.779,241.664 98.473,178.316 19.256,-23.166 36.671,-41.335 36.671,-41.335" | ||
1132 | 230 | id="path3101" /> | ||
1133 | 231 | <path | ||
1134 | 232 | inkscape:connector-curvature="0" | ||
1135 | 233 | d="m 260.349,26.207 c -5.591,1.753 89.848,-34.889 144.087,34.417 19.159,24.484 -3.043,124.519 -56.153,203.329" | ||
1136 | 234 | id="path3103" /> | ||
1137 | 235 | <path | ||
1138 | 236 | inkscape:connector-curvature="0" | ||
1139 | 237 | style="stroke-linejoin:bevel" | ||
1140 | 238 | d="m 348.282,263.953 c 0,0 3.461,17.036 53.764,6.653 22.04,-4.552 8.776,12.774 -13.577,23.155 -18.345,8.514 -59.474,10.696 -60.146,-1.069 -1.729,-30.355 21.647,-21.133 19.96,-28.739 -1.525,-6.85 -11.979,-13.573 -18.894,-30.338 -6.037,-14.633 -82.796,-126.849 21.287,-110.183 3.813,-0.789 -27.146,-99.002 -124.553,-100.599 -97.385,-1.597 -94.19,119.762 -94.19,119.762" | ||
1141 | 239 | id="path3105" /> | ||
1142 | 240 | <path | ||
1143 | 241 | inkscape:connector-curvature="0" | ||
1144 | 242 | d="m 188.604,274.334 c -13.577,15.166 -9.584,17.829 -36.723,23.417 -27.459,5.66 -11.326,15.733 -0.797,18.365 12.768,3.195 42.307,7.718 62.266,-20.229 6.078,-8.509 -0.036,-22.086 -8.385,-25.547 -4.034,-1.671 -9.428,-3.765 -16.361,3.994 z" | ||
1145 | 243 | id="path3107" /> | ||
1146 | 244 | <path | ||
1147 | 245 | inkscape:connector-curvature="0" | ||
1148 | 246 | d="m 187.715,274.069 c -1.368,-8.917 2.93,-19.528 7.536,-31.942 6.922,-18.626 22.893,-37.255 10.117,-96.339 -9.523,-44.029 -73.396,-9.163 -73.436,-3.193 -0.039,5.968 2.889,30.26 -1.067,58.548 -5.162,36.913 23.488,68.132 56.479,64.938" | ||
1149 | 247 | id="path3109" /> | ||
1150 | 248 | <path | ||
1151 | 249 | inkscape:connector-curvature="0" | ||
1152 | 250 | style="fill:#ffffff;stroke-width:4.15500021;stroke-linecap:butt;stroke-linejoin:miter" | ||
1153 | 251 | d="m 172.517,141.7 c -0.288,2.039 3.733,7.48 8.976,8.207 5.234,0.73 9.714,-3.522 9.998,-5.559 0.284,-2.039 -3.732,-4.285 -8.977,-5.015 -5.237,-0.731 -9.719,0.333 -9.996,2.367 z" | ||
1154 | 252 | id="path3111" /> | ||
1155 | 253 | <path | ||
1156 | 254 | inkscape:connector-curvature="0" | ||
1157 | 255 | style="fill:#ffffff;stroke-width:2.0775001;stroke-linecap:butt;stroke-linejoin:miter" | ||
1158 | 256 | d="m 331.941,137.543 c 0.284,2.039 -3.732,7.48 -8.976,8.207 -5.238,0.73 -9.718,-3.522 -10.005,-5.559 -0.277,-2.039 3.74,-4.285 8.979,-5.015 5.239,-0.73 9.718,0.333 10.002,2.368 z" | ||
1159 | 257 | id="path3113" /> | ||
1160 | 258 | <path | ||
1161 | 259 | inkscape:connector-curvature="0" | ||
1162 | 260 | d="m 350.676,123.432 c 0.863,15.994 -3.445,26.888 -3.988,43.914 -0.804,24.748 11.799,53.074 -7.191,81.435" | ||
1163 | 261 | id="path3115" /> | ||
1164 | 262 | <path | ||
1165 | 263 | inkscape:connector-curvature="0" | ||
1166 | 264 | style="stroke-width:3" | ||
1167 | 265 | d="M 0,60.232" | ||
1168 | 266 | id="path3117" /> | ||
1169 | 267 | </g> | ||
1170 | 268 | <rect | ||
1171 | 269 | style="fill:#4d4d4d;stroke:#000000;stroke-width:0.12356114" | ||
1172 | 270 | id="rect3015" | ||
1173 | 271 | width="40.634895" | ||
1174 | 272 | height="18.884109" | ||
1175 | 273 | x="8.1120977" | ||
1176 | 274 | y="67.328201" /> | ||
1177 | 275 | <text | ||
1178 | 276 | xml:space="preserve" | ||
1179 | 277 | style="font-size:11.4219265px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Courier 10 Pitch;-inkscape-font-specification:Courier 10 Pitch" | ||
1180 | 278 | x="11.078934" | ||
1181 | 279 | y="72.209114" | ||
1182 | 280 | id="text3011-6" | ||
1183 | 281 | sodipodi:linespacing="125%" | ||
1184 | 282 | transform="scale(0.9026139,1.1078934)"><tspan | ||
1185 | 283 | sodipodi:role="line" | ||
1186 | 284 | id="tspan3013-6" | ||
1187 | 285 | x="11.078934" | ||
1188 | 286 | y="72.209114">psql>_</tspan></text> | ||
1189 | 287 | </g> | ||
1190 | 288 | <g | ||
1191 | 289 | inkscape:groupmode="layer" | ||
1192 | 290 | id="layer2" | ||
1193 | 291 | inkscape:label="BADGE" | ||
1194 | 292 | style="display:none" | ||
1195 | 293 | sodipodi:insensitive="true"> | ||
1196 | 294 | <g | ||
1197 | 295 | style="display:inline" | ||
1198 | 296 | transform="translate(-340.00001,-581)" | ||
1199 | 297 | id="g4394" | ||
1200 | 298 | clip-path="none"> | ||
1201 | 299 | <g | ||
1202 | 300 | id="g855"> | ||
1203 | 301 | <g | ||
1204 | 302 | inkscape:groupmode="maskhelper" | ||
1205 | 303 | id="g870" | ||
1206 | 304 | clip-path="url(#clipPath873)" | ||
1207 | 305 | style="opacity:0.6;filter:url(#filter891)"> | ||
1208 | 306 | <path | ||
1209 | 307 | transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)" | ||
1210 | 308 | d="m 264,552.36218 a 12,12 0 1 1 -24,0 12,12 0 1 1 24,0 z" | ||
1211 | 309 | sodipodi:ry="12" | ||
1212 | 310 | sodipodi:rx="12" | ||
1213 | 311 | sodipodi:cy="552.36218" | ||
1214 | 312 | sodipodi:cx="252" | ||
1215 | 313 | id="path844" | ||
1216 | 314 | style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||
1217 | 315 | sodipodi:type="arc" /> | ||
1218 | 316 | </g> | ||
1219 | 317 | <g | ||
1220 | 318 | id="g862"> | ||
1221 | 319 | <path | ||
1222 | 320 | sodipodi:type="arc" | ||
1223 | 321 | style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||
1224 | 322 | id="path4398" | ||
1225 | 323 | sodipodi:cx="252" | ||
1226 | 324 | sodipodi:cy="552.36218" | ||
1227 | 325 | sodipodi:rx="12" | ||
1228 | 326 | sodipodi:ry="12" | ||
1229 | 327 | d="m 264,552.36218 a 12,12 0 1 1 -24,0 12,12 0 1 1 24,0 z" | ||
1230 | 328 | transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" /> | ||
1231 | 329 | <path | ||
1232 | 330 | transform="matrix(1.25,0,0,1.25,33,-100.45273)" | ||
1233 | 331 | d="m 264,552.36218 a 12,12 0 1 1 -24,0 12,12 0 1 1 24,0 z" | ||
1234 | 332 | sodipodi:ry="12" | ||
1235 | 333 | sodipodi:rx="12" | ||
1236 | 334 | sodipodi:cy="552.36218" | ||
1237 | 335 | sodipodi:cx="252" | ||
1238 | 336 | id="path4400" | ||
1239 | 337 | style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||
1240 | 338 | sodipodi:type="arc" /> | ||
1241 | 339 | <path | ||
1242 | 340 | sodipodi:type="star" | ||
1243 | 341 | style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||
1244 | 342 | id="path4459" | ||
1245 | 343 | sodipodi:sides="5" | ||
1246 | 344 | sodipodi:cx="666.19574" | ||
1247 | 345 | sodipodi:cy="589.50385" | ||
1248 | 346 | sodipodi:r1="7.2431178" | ||
1249 | 347 | sodipodi:r2="4.3458705" | ||
1250 | 348 | sodipodi:arg1="1.0471976" | ||
1251 | 349 | sodipodi:arg2="1.6755161" | ||
1252 | 350 | inkscape:flatsided="false" | ||
1253 | 351 | inkscape:rounded="0.1" | ||
1254 | 352 | inkscape:randomized="0" | ||
1255 | 353 | d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 -0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z" | ||
1256 | 354 | transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" /> | ||
1257 | 355 | </g> | ||
1258 | 356 | </g> | ||
1259 | 357 | </g> | ||
1260 | 358 | </g> | ||
1261 | 359 | </svg> |
Lines 755-760 are the new feature.