forked from MeticulousHome/meticulous-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathota.py
More file actions
249 lines (207 loc) · 9.1 KB
/
Copy pathota.py
File metadata and controls
249 lines (207 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import subprocess
import os
import re
from pathlib import Path
from datetime import datetime
from log import MeticulousLogger
from config import (
MeticulousConfig,
CONFIG_USER,
UPDATE_CHANNEL,
CONFIG_SYSTEM,
LAST_SYSTEM_VERSIONS,
)
logger = MeticulousLogger.getLogger(__name__)
HAWKBIT_CONFIG_DIR = "/etc/hawkbit/"
HAWKBIT_CHANNEL_FILE = "channel"
BUILD_DATE_FILE = "/opt/ROOTFS_BUILD_DATE"
REPO_INFO_FILE = "/opt/summary.txt"
BUILD_CHANNEL_FILE = "/opt/image-build-channel"
BUILD_VERSION_FILE = "/opt/image-build-version"
class UpdateManager:
ROOTFS_BUILD_DATE = None
CHANNEL = None
REPO_INFO = None
VERSION = None
is_changed = False
@staticmethod
def init():
build_channel = UpdateManager.getImageChannel()
if build_channel is None:
logger.error("Could not get build channel")
return
if MeticulousConfig[CONFIG_USER][UPDATE_CHANNEL] == "":
if build_channel in ["stable", "factory"]:
MeticulousConfig[CONFIG_USER][UPDATE_CHANNEL] = build_channel
else:
MeticulousConfig[CONFIG_USER][UPDATE_CHANNEL] = "stable"
MeticulousConfig.save()
logger.warning(f"Set update channel to {build_channel} based on image")
UpdateManager.setChannel(MeticulousConfig[CONFIG_USER][UPDATE_CHANNEL])
build_time = UpdateManager.getBuildTimestamp()
if build_time is None:
logger.error("Could not get build timestamp")
return
this_build_time = build_time.strftime("%Y%m%d_%H%M%S")
this_version_string = build_channel + "-" + this_build_time
try:
# We might not have anything in the list, so we accept the exception
last_known_version = MeticulousConfig[CONFIG_SYSTEM][LAST_SYSTEM_VERSIONS][-1]
is_changed = last_known_version != this_version_string
except IndexError:
is_changed = 1
last_known_version = "NONE"
print(f"Last known version image: {last_known_version}")
print(f"This image version: {this_version_string}")
if is_changed:
logger.info(
f"System was updated to {this_version_string} from {last_known_version}"
)
MeticulousConfig[CONFIG_SYSTEM][LAST_SYSTEM_VERSIONS].append(this_version_string)
while len(MeticulousConfig[CONFIG_SYSTEM][LAST_SYSTEM_VERSIONS]) > 30:
MeticulousConfig[CONFIG_SYSTEM][LAST_SYSTEM_VERSIONS].pop(0)
MeticulousConfig.save()
@staticmethod
def setChannel(channel: str):
channel = channel.strip()
hawkbit_dir = Path(HAWKBIT_CONFIG_DIR)
if not Path(hawkbit_dir).exists():
logger.info(f"{hawkbit_dir} does not exist, not changing update channel")
return
channel_file = hawkbit_dir.joinpath(HAWKBIT_CHANNEL_FILE)
try:
with open(channel_file, "r") as f:
current_channel = f.read().strip()
except FileNotFoundError:
current_channel = None
if current_channel != channel:
try:
with open(channel_file, "w") as f:
f.write(channel + "\n")
logger.info(f"Changed update channel from {current_channel} to {channel}")
subprocess.run(["systemctl", "restart", "rauc-hawkbit-updater"])
except Exception as e:
logger.error(f"Failed to change update channel: {e}")
@staticmethod
def getBuildTimestamp():
if UpdateManager.ROOTFS_BUILD_DATE is not None:
return UpdateManager.ROOTFS_BUILD_DATE
if not os.path.exists(BUILD_DATE_FILE):
logger.warning(f"{BUILD_DATE_FILE} file not found")
return None
with open(BUILD_DATE_FILE, "r") as file:
date_string = file.read().strip()
try:
build_time = datetime.strptime(date_string, "%a, %d %b %Y %H:%M:%S %z")
return build_time
except ValueError:
logger.error("Invalid date format in BUILD_DATE file")
return None
@staticmethod
def getImageChannel():
if UpdateManager.CHANNEL is not None:
return UpdateManager.CHANNEL
try:
with open(BUILD_CHANNEL_FILE, "r") as file:
UpdateManager.CHANNEL = file.read().strip()
logger.info(f"Read image build channel: {UpdateManager.CHANNEL}")
except FileNotFoundError:
logger.warning(f"{BUILD_CHANNEL_FILE} file not found")
except Exception as e:
logger.error(f"Error reading image build channel: {e}")
return UpdateManager.CHANNEL
@staticmethod
def getImageVersion():
if UpdateManager.VERSION is not None:
return UpdateManager.VERSION
try:
with open(BUILD_VERSION_FILE, "r") as file:
UpdateManager.VERSION = file.read().strip()
logger.info(f"Read image build Version: {UpdateManager.VERSION}")
except FileNotFoundError:
logger.warning(f"{BUILD_VERSION_FILE} file not found")
except Exception as e:
logger.error(f"Error reading image build Version: {e}")
return UpdateManager.VERSION
@staticmethod
def parse_summary_file(summary: str):
data = {}
current_repo_key = None
modified_files_section = False
# Regex to match lines like: ## ${COMPONENT} ##
repo_header_pattern = re.compile(r"^##\s+(.*?)\s+##")
lines = summary.splitlines()
for i, line in enumerate(lines):
line = line.strip()
# If the line matches something like "## watcher ##"
header_match = repo_header_pattern.match(line)
if header_match:
current_repo_key = header_match.group(1)
data[current_repo_key] = {
"repo": None,
"url": None,
"branch": None,
"commit": None,
"last_commit": None,
"modified_files": [],
}
modified_files_section = False
continue
# If we haven't identified a repository block yet, skip
if current_repo_key is None:
continue
# Check for lines like "Repository: xyz", "URL: xyz", etc.
if line.startswith("Repository:"):
data[current_repo_key]["repo"] = line.replace("Repository:", "").strip()
modified_files_section = False
elif line.startswith("URL:"):
data[current_repo_key]["url"] = line.replace("URL:", "").strip()
modified_files_section = False
elif line.startswith("Branch:"):
data[current_repo_key]["branch"] = line.replace("Branch:", "").strip()
modified_files_section = False
elif line.startswith("Commit:"):
data[current_repo_key]["commit"] = line.replace("Commit:", "").strip()
modified_files_section = False
elif line.startswith("Last commit details:"):
if len(lines) > i + 1:
data[current_repo_key]["last_commit"] = lines[i + 1].strip()
modified_files_section = False
elif line.startswith("Modified files:"):
# After this line, all subsequent non-empty lines belong to the Modified files list,
# until the next repo block or blank line.
modified_files_section = True
else:
# If we're in the modified files section, any non-empty line is a filename
if modified_files_section and line:
data[current_repo_key]["modified_files"].append(line)
return data
@staticmethod
def getRepositoryInfo():
if UpdateManager.REPO_INFO is not None:
return UpdateManager.REPO_INFO
try:
with open(REPO_INFO_FILE, "r") as file:
summary_file = file.read().strip()
UpdateManager.REPO_INFO = UpdateManager.parse_summary_file(summary_file)
logger.info(f"Read repository info: {UpdateManager.REPO_INFO}")
except FileNotFoundError:
logger.warning(f"{REPO_INFO_FILE} file not found")
except Exception as e:
logger.error(f"Error reading repository info: {e}")
return UpdateManager.REPO_INFO
@staticmethod
def forward_time():
target_time = UpdateManager.getBuildTimestamp()
if target_time is None:
logger.error("Could not get build timestamp")
return
current_time = datetime.now(target_time.tzinfo)
# Forward time only if it is older than the image itself
if current_time < target_time:
formatted_time = target_time.strftime("%Y-%m-%d %H:%M:%S")
try:
subprocess.run(["date", "-s", formatted_time], check=True)
print(f"System time updated to: {formatted_time}")
except subprocess.CalledProcessError as e:
print(f"Failed to set system time: {e}")