1
|
|
|
"""Data structures for computer information.""" |
2
|
|
|
|
3
|
1 |
|
import socket |
4
|
1 |
|
import uuid |
5
|
1 |
|
|
6
|
|
|
import log |
7
|
1 |
|
import yorm |
8
|
|
|
|
9
|
1 |
|
from ._bases import NameMixin |
10
|
|
|
|
11
|
|
|
|
12
|
1 |
|
@yorm.attr(name=yorm.types.String) |
13
|
|
|
@yorm.attr(hostname=yorm.types.String) |
14
|
|
|
@yorm.attr(address=yorm.types.String) |
15
|
1 |
|
class Computer(NameMixin, yorm.types.AttributeDictionary): |
16
|
1 |
|
"""A dictionary of identifying computer information.""" |
17
|
1 |
|
|
18
|
1 |
|
def __init__(self, name=None, hostname=None, address=None): |
19
|
|
|
super().__init__() |
20
|
|
|
self.name = name |
21
|
1 |
|
self.address = address or self.get_address() |
22
|
1 |
|
self.hostname = hostname or self.get_hostname() |
23
|
1 |
|
|
24
|
1 |
|
@staticmethod |
25
|
1 |
|
def get_address(node=None): |
26
|
|
|
"""Get this computer's MAC address.""" |
27
|
1 |
|
if node is None: |
28
|
1 |
|
node = uuid.getnode() |
29
|
|
|
return ':'.join(("%012X" % node)[i : i + 2] for i in range(0, 12, 2)) |
30
|
1 |
|
|
31
|
1 |
|
@staticmethod |
32
|
1 |
|
def get_hostname(): |
33
|
|
|
"""Get this computer's hostname.""" |
34
|
1 |
|
return socket.gethostname() |
35
|
|
|
|
36
|
|
|
|
37
|
1 |
|
@yorm.attr(all=Computer) |
38
|
|
|
class Computers(yorm.types.SortedList): |
39
|
|
|
"""A list of computers.""" |
40
|
1 |
|
|
41
|
1 |
|
@property |
42
|
|
|
def names(self): |
43
|
|
|
"""Get a list of all computers' labels.""" |
44
|
1 |
|
return [c.name for c in self] |
45
|
|
|
|
46
|
|
|
def get(self, name): |
47
|
1 |
|
"""Get the computer with the given name.""" |
48
|
|
|
computer = self.find(name) |
49
|
1 |
|
assert computer, name |
50
|
|
|
return computer |
51
|
1 |
|
|
52
|
1 |
|
def find(self, name): |
53
|
1 |
|
"""Find the computer with the given name, else None.""" |
54
|
|
|
log.debug("Finding computer for '%s'...", name) |
55
|
1 |
|
for computer in self: |
56
|
|
|
if computer == name: |
57
|
1 |
|
return computer |
58
|
1 |
|
return None |
59
|
1 |
|
|
60
|
1 |
|
def match(self, partial): |
61
|
|
|
"""Find a computer with a similar name.""" |
62
|
1 |
|
log.debug("Finding computer similar to '%s'...", partial) |
63
|
|
|
for computer in self: |
64
|
1 |
|
if partial.lower() in computer.name.lower(): |
65
|
1 |
|
return computer |
66
|
1 |
|
return None |
67
|
1 |
|
|
68
|
|
|
def get_current(self): |
69
|
1 |
|
"""Get the current computer's information.""" |
70
|
|
|
this = Computer(None) |
71
|
1 |
|
|
72
|
|
|
# Search for a matching address |
73
|
|
|
for other in self: |
74
|
1 |
|
if this.address == other.address: |
75
|
1 |
|
other.hostname = this.hostname |
76
|
1 |
|
return other |
77
|
1 |
|
|
78
|
|
|
# Else, search for a matching hostname |
79
|
|
|
for other in self: |
80
|
1 |
|
if this.hostname == other.hostname: |
81
|
1 |
|
other.address = this.address |
82
|
1 |
|
return other |
83
|
1 |
|
|
84
|
|
|
# Else, this is a new computer |
85
|
|
|
this.name = self.generate_name(this) |
86
|
1 |
|
assert this.name != 'localhost' |
87
|
1 |
|
log.debug("New computer: %s", this) |
88
|
1 |
|
self.append(this) |
89
|
1 |
|
return this |
90
|
1 |
|
|
91
|
|
|
def generate_name(self, computer): |
92
|
1 |
|
"""Generate a new label for a computer.""" |
93
|
|
|
name = computer.hostname.lower().split('.')[0] |
94
|
1 |
|
copy = 1 |
95
|
1 |
|
while name in self.names: |
96
|
1 |
|
copy += 1 |
97
|
1 |
|
name2 = "{}-{}".format(name, copy) |
98
|
1 |
|
if name2 not in self.names: |
99
|
1 |
|
name = name2 |
100
|
|
|
return name |
101
|
|
|
|