Coretex
utils.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from typing import Optional, Tuple, Any, Dict
19 
20 from . import config_defaults
21 from ..networking import networkManager, NetworkRequestError
22 from ..utils.docker import DockerConfigurationException
23 
24 
25 def validateRamField(ram: Optional[Any], ramLimit: int) -> Optional[Tuple[int, str]]:
26  if ramLimit < config_defaults.MINIMUM_RAM:
27  raise DockerConfigurationException(
28  f"Minimum Node RAM requirement ({config_defaults.MINIMUM_RAM}GB) "
29  f"is higher than your current Docker desktop RAM limit ({ramLimit}GB). "
30  "Please adjust resource limitations in Docker Desktop settings to match Node requirements."
31  )
32 
33  defaultRamValue = int(min(max(config_defaults.MINIMUM_RAM, ramLimit), config_defaults.DEFAULT_RAM))
34 
35  if not isinstance(ram, int):
36  message = (
37  f"Invalid config \"ram\" field type \"{type(ram)}\". Expected: \"int\""
38  f"Using default value of {defaultRamValue} GB"
39  )
40 
41  return defaultRamValue, message
42 
43  if ram < config_defaults.MINIMUM_RAM:
44  message = (
45  f"WARNING: Minimum Node RAM requirement ({config_defaults.MINIMUM_RAM}GB) "
46  f"is higher than the configured value ({ram}GB)"
47  f"Overriding \"ram\" field to match node RAM requirements."
48  )
49 
50  return defaultRamValue, message
51 
52  if ram > ramLimit:
53  message = (
54  f"WARNING: RAM limit in Docker Desktop ({ramLimit}GB) "
55  f"is lower than the configured value ({ram}GB)"
56  f"Overriding \"ram\" field to limit in Docker Desktop."
57  )
58 
59  return defaultRamValue, message
60 
61  return None
62 
63 def validateCpuCount(cpuCount: Optional[Any], cpuLimit: int) -> Optional[Tuple[int, str]]:
64  defaultCpuCount = config_defaults.DEFAULT_CPU_COUNT if config_defaults.DEFAULT_CPU_COUNT <= cpuLimit else cpuLimit
65 
66  if not isinstance(cpuCount, int):
67  message = (
68  f"Invalid config \"cpuCount\" field type \"{type(cpuCount)}\". Expected: \"int\""
69  f"Using default value of {defaultCpuCount} cores"
70  )
71 
72  return defaultCpuCount, message
73 
74  if cpuCount > cpuLimit:
75  message = (
76  f"WARNING: CPU limit in Docker Desktop ({cpuLimit}) "
77  f"is lower than the configured value ({cpuCount})"
78  f"Overriding \"cpuCount\" field to limit in Docker Desktop."
79  )
80 
81  return defaultCpuCount, message
82 
83  return None
84 
85 def validateSwapMemory(swap: Optional[Any], swapLimit: int) -> Optional[Tuple[int, str]]:
86  defaultSwapMemory = config_defaults.DEFAULT_SWAP_MEMORY if config_defaults.DEFAULT_SWAP_MEMORY <= swapLimit else swapLimit
87 
88  if not isinstance(swap, int):
89  message = (
90  f"Invalid config \"swap\" field type \"{type(swap)}\". Expected: \"int\""
91  f"Using default value of {defaultSwapMemory} GB"
92  )
93 
94  return defaultSwapMemory, message
95 
96  if swap > swapLimit:
97  message = (
98  f"WARNING: SWAP limit in Docker Desktop ({swapLimit}GB) "
99  f"is lower than the configured value ({swap}GB)"
100  f"Overriding \"swap\" field to limit in Docker Desktop."
101  )
102 
103  return defaultSwapMemory, message
104 
105  return None
106 
107 
108 def fetchNodeId(name: str) -> int:
109  params = {
110  "name": f"={name}"
111  }
112 
113  response = networkManager.get("service/directory", params)
114  if response.hasFailed():
115  raise NetworkRequestError(response, "Failed to fetch node id.")
116 
117  responseJson = response.getJson(dict)
118  data = responseJson.get("data")
119 
120  if not isinstance(data, list):
121  raise TypeError(f"Invalid \"data\" type {type(data)}. Expected: \"list\"")
122 
123  if len(data) == 0:
124  raise ValueError(f"Node with name \"{name}\" not found.")
125 
126  nodeJson = data[0]
127  if not isinstance(nodeJson, dict):
128  raise TypeError(f"Invalid \"nodeJson\" type {type(nodeJson)}. Expected: \"dict\"")
129 
130  id = nodeJson.get("id")
131  if not isinstance(id, int):
132  raise TypeError(f"Invalid \"id\" type {type(id)}. Expected: \"int\"")
133 
134  return id
135 
136 
137 def fetchInitialData() -> Dict[str, Any]:
138  response = networkManager.get("user/initial-data")
139 
140  if response.hasFailed():
141  raise NetworkRequestError(response, "Failed to fetch user's initial data.")
142 
143  return response.getJson(dict)