5604de676c0ded41ce5edcc051809ac7968c6286
[python_utils.git] / site_config.py
1 #!/usr/bin/env python3
2
3 # © Copyright 2021-2022, Scott Gasch
4
5 """Location/site dependent data."""
6
7 import logging
8 import platform
9 from dataclasses import dataclass
10 from typing import Callable, Optional
11
12 # Note: this module is fairly early loaded.  Be aware of dependencies.
13 import config
14 from type.locations import Location
15
16 logger = logging.getLogger(__name__)
17
18 args = config.add_commandline_args(
19     f'Global Site Config ({__file__})',
20     'Args related to global site-specific configuration',
21 )
22 args.add_argument(
23     '--site_config_override_location',
24     default='NONE',
25     const='NONE',
26     nargs='?',
27     choices=['HOUSE', 'CABIN', 'NONE'],
28     help='Where are we, HOUSE, CABIN?  Overrides standard detection code.',
29 )
30
31
32 @dataclass
33 class SiteConfig(object):
34     """The set of information specific to where the program is running."""
35
36     location_name: str
37     location: Location
38     network: str
39     network_netmask: str
40     network_router_ip: str
41     presence_location: Location
42     is_anyone_present: Callable
43     arper_minimum_device_count: int
44     arper_cache_file: str
45
46
47 def get_location_name():
48     """
49     Where are we?
50
51     >>> location = get_location_name()
52     >>> location == 'HOUSE' or location == 'CABIN'
53     True
54
55     """
56     return get_config().location_name
57
58
59 def get_location():
60     """
61     Returns location as an enum instead of a string.
62
63     >>> from type.locations import Location
64     >>> location = get_location()
65     >>> location == Location.HOUSE or location == Location.CABIN
66     True
67
68     """
69     return get_config().location
70
71
72 def is_anyone_present_wrapper(location: Location):
73     import base_presence
74
75     p = base_presence.PresenceDetection()
76     return p.is_anyone_in_location_now(location)
77
78
79 def other_location() -> str:
80     """
81     Returns the location where this program is _NOT_ running.
82
83     >>> x = other_location()
84     >>> x in set(['HOUSE', 'CABIN'])
85     True
86
87     >>> y = this_location()
88     >>> x == y
89     False
90
91     """
92     this = this_location()
93     if this == 'HOUSE':
94         return 'CABIN'
95     elif this == 'CABIN':
96         return 'HOUSE'
97     else:
98         raise Exception(f"{this} doesn't tell me where I'm running?!")
99
100
101 def this_location() -> str:
102     """
103     Returns the location where this program _IS_ running.
104
105     >>> x = this_location()
106     >>> x in set(['HOUSE', 'CABIN'])
107     True
108
109     """
110     hostname = platform.node()
111     if '.house' in hostname:
112         location = 'HOUSE'
113     elif '.cabin' in hostname:
114         location = 'CABIN'
115     elif '.local' in hostname:
116         location = 'HOUSE'
117     else:
118         raise Exception(f"{hostname} doesn't help me know where I'm running?!")
119     return location
120
121
122 def effective_location(location_override: Optional[str] = None) -> str:
123     """Detects and returns a location taking into account two override
124     mechanisms.
125
126     >>> x = effective_location()
127     >>> x in set(['HOUSE', 'CABIN'])
128     True
129
130     >>> effective_location('HOUSE')
131     'HOUSE'
132
133     """
134     if location_override is None:
135         try:
136             location_override = config.config['site_config_override_location']
137         except KeyError:
138             location_override = None
139
140     if location_override is None or location_override == 'NONE':
141         location = this_location()
142     else:
143         logger.debug('site_config\'s location_override was set to: %s', location_override)
144         location = location_override
145     return location
146
147
148 def get_config(location_override: Optional[str] = None):
149     """
150     Get a configuration dataclass with information that is
151     site-specific including the current running location.
152
153     >>> cfg = get_config()
154     >>> cfg.location_name == 'HOUSE' or cfg.location_name == 'CABIN'
155     True
156
157     """
158     location = effective_location(location_override)
159     if location == 'HOUSE':
160         return SiteConfig(
161             location_name='HOUSE',
162             location=Location.HOUSE,
163             network='10.0.0.0/24',
164             network_netmask='255.255.255.0',
165             network_router_ip='10.0.0.1',
166             presence_location=Location.HOUSE,
167             is_anyone_present=lambda x=Location.HOUSE: is_anyone_present_wrapper(x),
168             arper_minimum_device_count=50,
169             arper_cache_file='/home/scott/cache/.arp_table_cache_house',
170         )
171     elif location == 'CABIN':
172         return SiteConfig(
173             location_name='CABIN',
174             location=Location.CABIN,
175             network='192.168.0.0/24',
176             network_netmask='255.255.255.0',
177             network_router_ip='192.168.0.1',
178             presence_location=Location.CABIN,
179             is_anyone_present=lambda x=Location.CABIN: is_anyone_present_wrapper(x),
180             arper_minimum_device_count=15,
181             arper_cache_file='/home/scott/cache/.arp_table_cache_cabin',
182         )
183     else:
184         raise Exception(f'Unknown site location: {location}')
185
186
187 if __name__ == '__main__':
188     import doctest
189
190     doctest.testmod()