mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-31 08:34:04 -05:00
changes
This commit is contained in:
1 parent
a2365ca78a
commit
f8d3b0fc6a
7 files changed
+166
-56
No files matched your search
Generated
+10
-21
@@ -3288,36 +3288,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.25.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
|
||||
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
||||
dependencies = [
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.25.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
|
||||
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.25.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
|
||||
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
@@ -3325,9 +3321,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.25.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
|
||||
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
@@ -3337,13 +3333,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.25.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
|
||||
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
@@ -4742,12 +4737,6 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ sysinfo = { version = "0", default-features = false, features = [ "system" ] }
|
||||
default-net = "0"
|
||||
reqwest = { version = "0.13", default-features = false, features = ["blocking", "json", "rustls-no-provider", "charset", "http2", "system-proxy"] }
|
||||
rustls = "0.23"
|
||||
pyo3 = "0.25.1"
|
||||
pyo3 = "0.29.0"
|
||||
colored = "2"
|
||||
miniz_oxide = "0.8"
|
||||
byteorder = "1"
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct DeviceWeightRequest {
|
||||
/// This struct is used to receive a response from the Long Term Stats API
|
||||
/// It contains the circuit_id and the weight of the device
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[pyclass]
|
||||
#[pyclass(skip_from_py_object)]
|
||||
pub struct DeviceWeightResponse {
|
||||
#[pyo3(get)]
|
||||
pub circuit_id: String,
|
||||
|
||||
@@ -36,6 +36,8 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
type PyObject = Py<PyAny>;
|
||||
|
||||
// ===== Planner CBOR I/O =====
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
@@ -166,11 +168,11 @@ fn plan_top_level_cpu_bins(
|
||||
hysteresis_threshold: Option<f64>,
|
||||
) -> PyResult<PyObject> {
|
||||
let items_any = items.bind(py);
|
||||
let items_list = items_any.downcast::<PyList>()?;
|
||||
let items_list = items_any.cast::<PyList>()?;
|
||||
let planner_items: Vec<lqos_config::TopLevelPlannerItem> = items_list
|
||||
.iter()
|
||||
.map(|item| -> PyResult<lqos_config::TopLevelPlannerItem> {
|
||||
let dict = item.downcast::<PyDict>()?;
|
||||
let dict = item.cast::<PyDict>()?;
|
||||
let id = get_string(dict, "id", String::new());
|
||||
let weight = get_f64(dict, "weight", 1.0);
|
||||
Ok(lqos_config::TopLevelPlannerItem { id, weight })
|
||||
@@ -180,7 +182,7 @@ fn plan_top_level_cpu_bins(
|
||||
let prev_assign_map = match prev_assign {
|
||||
Some(obj) => {
|
||||
let any = obj.bind(py);
|
||||
let dict = any.downcast::<PyDict>()?;
|
||||
let dict = any.cast::<PyDict>()?;
|
||||
dict.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
Some((k.extract::<String>().ok()?, v.extract::<String>().ok()?))
|
||||
@@ -192,7 +194,7 @@ fn plan_top_level_cpu_bins(
|
||||
let last_change_map = match last_change_ts {
|
||||
Some(obj) => {
|
||||
let any = obj.bind(py);
|
||||
let dict = any.downcast::<PyDict>()?;
|
||||
let dict = any.cast::<PyDict>()?;
|
||||
dict.iter()
|
||||
.filter_map(|(k, v)| Some((k.extract::<String>().ok()?, v.extract::<f64>().ok()?)))
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
@@ -258,11 +260,11 @@ fn plan_class_identities(
|
||||
stick_offset: u16,
|
||||
circuit_padding: u32,
|
||||
) -> PyResult<PyObject> {
|
||||
let sites_list = sites.bind(py).downcast::<PyList>()?;
|
||||
let sites_list = sites.bind(py).cast::<PyList>()?;
|
||||
let site_inputs: Vec<lqos_config::SiteIdentityInput> = sites_list
|
||||
.iter()
|
||||
.map(|item| -> PyResult<lqos_config::SiteIdentityInput> {
|
||||
let dict = item.downcast::<PyDict>()?;
|
||||
let dict = item.cast::<PyDict>()?;
|
||||
Ok(lqos_config::SiteIdentityInput {
|
||||
site_key: get_string(dict, "site_key", String::new()),
|
||||
parent_path: get_string(dict, "parent_path", String::new()),
|
||||
@@ -272,14 +274,14 @@ fn plan_class_identities(
|
||||
})
|
||||
.collect::<PyResult<Vec<_>>>()?;
|
||||
|
||||
let groups_list = circuit_groups.bind(py).downcast::<PyList>()?;
|
||||
let groups_list = circuit_groups.bind(py).cast::<PyList>()?;
|
||||
let circuit_group_inputs: Vec<lqos_config::CircuitIdentityGroupInput> = groups_list
|
||||
.iter()
|
||||
.map(|item| -> PyResult<lqos_config::CircuitIdentityGroupInput> {
|
||||
let dict = item.downcast::<PyDict>()?;
|
||||
let dict = item.cast::<PyDict>()?;
|
||||
let ids = match dict.get_item("circuit_ids")? {
|
||||
Some(value) => value
|
||||
.downcast::<PyList>()?
|
||||
.cast::<PyList>()?
|
||||
.iter()
|
||||
.filter_map(|entry| entry.extract::<String>().ok())
|
||||
.collect::<Vec<_>>(),
|
||||
@@ -295,11 +297,11 @@ fn plan_class_identities(
|
||||
|
||||
let previous_sites = match site_state {
|
||||
Some(obj) => {
|
||||
let dict = obj.bind(py).downcast::<PyDict>()?;
|
||||
let dict = obj.bind(py).cast::<PyDict>()?;
|
||||
dict.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
let key = k.extract::<String>().ok()?;
|
||||
let entry = v.downcast::<PyDict>().ok()?;
|
||||
let entry = v.cast::<PyDict>().ok()?;
|
||||
Some((
|
||||
key,
|
||||
lqos_config::PlannerSiteIdentityState {
|
||||
@@ -318,11 +320,11 @@ fn plan_class_identities(
|
||||
|
||||
let previous_circuits = match circuit_state {
|
||||
Some(obj) => {
|
||||
let dict = obj.bind(py).downcast::<PyDict>()?;
|
||||
let dict = obj.bind(py).cast::<PyDict>()?;
|
||||
dict.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
let key = k.extract::<String>().ok()?;
|
||||
let entry = v.downcast::<PyDict>().ok()?;
|
||||
let entry = v.cast::<PyDict>().ok()?;
|
||||
Some((
|
||||
key,
|
||||
lqos_config::PlannerCircuitIdentityState {
|
||||
@@ -411,7 +413,7 @@ fn plan_class_identities(
|
||||
fn write_planner_cbor(py: Python, path: String, state: PyObject) -> PyResult<bool> {
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
let dict = state.downcast_bound::<pyo3::types::PyDict>(py)?;
|
||||
let dict = state.cast_bound::<pyo3::types::PyDict>(py)?;
|
||||
// Build strongly typed struct, preserving integer keys
|
||||
let algo_version = get_string(dict, "algo_version", default_algo_version());
|
||||
let updated_at = get_f64(dict, "updated_at", 0.0);
|
||||
@@ -420,7 +422,7 @@ fn write_planner_cbor(py: Python, path: String, state: PyObject) -> PyResult<boo
|
||||
let site_count = get_i64(dict, "site_count", 0);
|
||||
let mut site_names: Vec<i64> = Vec::new();
|
||||
if let Ok(Some(sn)) = dict.get_item("site_names")
|
||||
&& let Ok(list) = sn.downcast::<pyo3::types::PyList>()
|
||||
&& let Ok(list) = sn.cast::<pyo3::types::PyList>()
|
||||
{
|
||||
for item in list.iter() {
|
||||
if let Some(n) = to_i64_any(&item) {
|
||||
@@ -431,11 +433,11 @@ fn write_planner_cbor(py: Python, path: String, state: PyObject) -> PyResult<boo
|
||||
// site_map
|
||||
let mut site_map: BTreeMap<i64, PlannerSiteEntry> = BTreeMap::new();
|
||||
if let Ok(Some(sm_any)) = dict.get_item("site_map")
|
||||
&& let Ok(sm_dict) = sm_any.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(sm_dict) = sm_any.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
for (k, v) in sm_dict.iter() {
|
||||
if let Some(key) = to_i64_any(&k)
|
||||
&& let Ok(entry) = v.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(entry) = v.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
let cpu = get_i64(entry, "cpu", 0);
|
||||
let major = get_i64(entry, "major", 0);
|
||||
@@ -459,11 +461,11 @@ fn write_planner_cbor(py: Python, path: String, state: PyObject) -> PyResult<boo
|
||||
// circuit_map
|
||||
let mut circuit_map: BTreeMap<i64, PlannerCircuitEntry> = BTreeMap::new();
|
||||
if let Ok(Some(cm_any)) = dict.get_item("circuit_map")
|
||||
&& let Ok(cm_dict) = cm_any.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(cm_dict) = cm_any.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
for (k, v) in cm_dict.iter() {
|
||||
if let Some(key) = to_i64_any(&k)
|
||||
&& let Ok(entry) = v.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(entry) = v.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
let cpu = get_i64(entry, "cpu", 0);
|
||||
let major = get_i64(entry, "major", 0);
|
||||
@@ -740,7 +742,7 @@ fn fetch_planner_remote(
|
||||
#[pyfunction]
|
||||
fn store_planner_remote(py: Python, state: PyObject) -> PyResult<bool> {
|
||||
// Extract needed values and serialize as compressed CBOR
|
||||
let dict = state.downcast_bound::<pyo3::types::PyDict>(py)?;
|
||||
let dict = state.cast_bound::<pyo3::types::PyDict>(py)?;
|
||||
let algo_version = get_string(dict, "algo_version", default_algo_version());
|
||||
let updated_at = get_f64(dict, "updated_at", 0.0);
|
||||
let queues_available = get_i64(dict, "queuesAvailable", 0);
|
||||
@@ -749,7 +751,7 @@ fn store_planner_remote(py: Python, state: PyObject) -> PyResult<bool> {
|
||||
// site_names
|
||||
let mut site_names: Vec<i64> = Vec::new();
|
||||
if let Ok(Some(sn)) = dict.get_item("site_names")
|
||||
&& let Ok(list) = sn.downcast::<pyo3::types::PyList>()
|
||||
&& let Ok(list) = sn.cast::<pyo3::types::PyList>()
|
||||
{
|
||||
for item in list.iter() {
|
||||
if let Some(n) = to_i64_any(&item) {
|
||||
@@ -760,11 +762,11 @@ fn store_planner_remote(py: Python, state: PyObject) -> PyResult<bool> {
|
||||
// site_map
|
||||
let mut site_map: BTreeMap<i64, PlannerSiteEntry> = BTreeMap::new();
|
||||
if let Ok(Some(sm_any)) = dict.get_item("site_map")
|
||||
&& let Ok(sm_dict) = sm_any.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(sm_dict) = sm_any.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
for (k, v) in sm_dict.iter() {
|
||||
if let Some(key) = to_i64_any(&k)
|
||||
&& let Ok(entry) = v.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(entry) = v.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
let cpu = get_i64(entry, "cpu", 0);
|
||||
let major = get_i64(entry, "major", 0);
|
||||
@@ -788,11 +790,11 @@ fn store_planner_remote(py: Python, state: PyObject) -> PyResult<bool> {
|
||||
// circuit_map
|
||||
let mut circuit_map: BTreeMap<i64, PlannerCircuitEntry> = BTreeMap::new();
|
||||
if let Ok(Some(cm_any)) = dict.get_item("circuit_map")
|
||||
&& let Ok(cm_dict) = cm_any.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(cm_dict) = cm_any.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
for (k, v) in cm_dict.iter() {
|
||||
if let Some(key) = to_i64_any(&k)
|
||||
&& let Ok(entry) = v.downcast::<pyo3::types::PyDict>()
|
||||
&& let Ok(entry) = v.cast::<pyo3::types::PyDict>()
|
||||
{
|
||||
let cpu = get_i64(entry, "cpu", 0);
|
||||
let major = get_i64(entry, "major", 0);
|
||||
|
||||
@@ -2757,12 +2757,16 @@ fn resolved_auto_queue_visibility_policy(
|
||||
if !queue_auto_node_kind_can_hide(tree_node) {
|
||||
return TopologyQueueVisibilityPolicy::QueueVisible;
|
||||
}
|
||||
if child_branch_counts
|
||||
let logical_child_count = child_branch_counts
|
||||
.get(ui_node.node_id.as_str())
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
{
|
||||
.unwrap_or_default();
|
||||
let effective_child_count = tree_node
|
||||
.get("children")
|
||||
.and_then(Value::as_object)
|
||||
.map(Map::len)
|
||||
.unwrap_or_default();
|
||||
if logical_child_count == 0 && effective_child_count == 0 {
|
||||
return TopologyQueueVisibilityPolicy::QueueVisible;
|
||||
}
|
||||
let threshold = config.topology.queue_auto_virtualize_threshold_mbps;
|
||||
@@ -7036,6 +7040,36 @@ mod tests {
|
||||
assert!(aggregation_children.get("Access AP").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queue_auto_marks_large_ap_branch_virtual_with_effective_tree_children() {
|
||||
let (config, canonical, mut editor_state, effective) = ap_branch_fixture();
|
||||
let access_ap = editor_state
|
||||
.nodes
|
||||
.iter_mut()
|
||||
.find(|node| node.node_id == "ap-child")
|
||||
.expect("fixture should include Access AP");
|
||||
access_ap.current_parent_node_id = Some("site-root".to_string());
|
||||
access_ap.current_parent_node_name = Some("Core".to_string());
|
||||
|
||||
let effective_network = apply_effective_topology_to_network_json(
|
||||
&config,
|
||||
&canonical,
|
||||
&editor_state,
|
||||
&effective,
|
||||
);
|
||||
let root = effective_network
|
||||
.as_object()
|
||||
.expect("effective export should remain an object tree");
|
||||
let aggregation = root["Core"]["children"]["Aggregation Switch"]
|
||||
.as_object()
|
||||
.expect("Aggregation Switch should remain visible as a virtual node");
|
||||
|
||||
assert_eq!(
|
||||
aggregation.get("virtual").and_then(Value::as_bool),
|
||||
Some(true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queue_auto_keeps_large_ap_branch_visible_with_direct_circuit() {
|
||||
let (config, canonical, editor_state, effective) = ap_branch_fixture();
|
||||
|
||||
+54
-2
@@ -175,6 +175,15 @@ apt_candidate_has_hotfix_repo() {
|
||||
-v version="$version" \
|
||||
-v repo_url="$repo_url" \
|
||||
-v dist_component="${HOTFIX_REPO_DIST}/${HOTFIX_REPO_COMPONENT}" '
|
||||
function normalized_url(url) {
|
||||
sub(/\/+$/, "", url)
|
||||
return url
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
repo_url = normalized_url(repo_url)
|
||||
}
|
||||
|
||||
($1 == "***" && $2 == version) || ($1 == version && $2 ~ /^[0-9]+$/) {
|
||||
in_version = 1
|
||||
next
|
||||
@@ -184,7 +193,7 @@ apt_candidate_has_hotfix_repo() {
|
||||
in_version = 0
|
||||
}
|
||||
|
||||
in_version && $1 ~ /^[0-9]+$/ && $2 == repo_url && $3 == dist_component {
|
||||
in_version && $1 ~ /^[0-9]+$/ && normalized_url($2) == repo_url && $3 == dist_component {
|
||||
found = 1
|
||||
exit
|
||||
}
|
||||
@@ -193,6 +202,47 @@ apt_candidate_has_hotfix_repo() {
|
||||
'
|
||||
}
|
||||
|
||||
apt_madison_has_hotfix_repo() {
|
||||
local package="$1"
|
||||
local version="$2"
|
||||
local repo_url="${HOTFIX_REPO_URL%/}"
|
||||
|
||||
LC_ALL=C apt-cache madison "$package" | awk \
|
||||
-F '|' \
|
||||
-v version="$version" \
|
||||
-v repo_url="$repo_url" \
|
||||
-v dist_component="${HOTFIX_REPO_DIST}/${HOTFIX_REPO_COMPONENT}" '
|
||||
function trim(text) {
|
||||
gsub(/^[ \t]+|[ \t]+$/, "", text)
|
||||
return text
|
||||
}
|
||||
|
||||
function normalized_url(url) {
|
||||
sub(/\/+$/, "", url)
|
||||
return url
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
repo_url = normalized_url(repo_url)
|
||||
}
|
||||
|
||||
{
|
||||
candidate_version = trim($2)
|
||||
source = trim($3)
|
||||
split(source, source_parts, /[ \t]+/)
|
||||
|
||||
if (candidate_version == version &&
|
||||
normalized_url(source_parts[1]) == repo_url &&
|
||||
source_parts[2] == dist_component) {
|
||||
found = 1
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
END { if (!found) exit 1 }
|
||||
'
|
||||
}
|
||||
|
||||
resolve_hotfix_package_version() {
|
||||
local package version priority resolved_version
|
||||
|
||||
@@ -211,7 +261,9 @@ resolve_hotfix_package_version() {
|
||||
[[ "$priority" =~ ^[0-9]+$ ]] || fail "Unable to verify LibreQoS APT pin priority for $package=$version."
|
||||
(( priority >= 1001 )) || fail "APT candidate for $package=$version is not pinned from the LibreQoS hotfix repo."
|
||||
# apt-cache reports the pin on the version line; the matching source line can remain at archive priority 500.
|
||||
apt_candidate_has_hotfix_repo "$package" "$version" || fail "APT candidate for $package=$version is not from $HOTFIX_REPO_URL $HOTFIX_REPO_DIST/$HOTFIX_REPO_COMPONENT."
|
||||
apt_candidate_has_hotfix_repo "$package" "$version" || \
|
||||
apt_madison_has_hotfix_repo "$package" "$version" || \
|
||||
fail "APT candidate for $package=$version is not from $HOTFIX_REPO_URL $HOTFIX_REPO_DIST/$HOTFIX_REPO_COMPONENT."
|
||||
|
||||
if [[ -z "${resolved_version:-}" ]]; then
|
||||
resolved_version="$version"
|
||||
|
||||
@@ -77,10 +77,13 @@ class SystemdHotfixScriptTest(unittest.TestCase):
|
||||
#!/bin/sh
|
||||
[ "${HOTFIX_TEST_APT_CACHE_FAIL:-0}" = "1" ] && exit 12
|
||||
|
||||
command="$1"
|
||||
package="$2"
|
||||
version="${HOTFIX_TEST_CANDIDATE_VERSION:-255.4-1ubuntu9999+libreqos1}"
|
||||
priority="${HOTFIX_TEST_CANDIDATE_PRIORITY:-1001}"
|
||||
repo_line="${HOTFIX_TEST_REPO_LINE:- 500 https://repo.libreqos.com noble/main amd64 Packages}"
|
||||
madison_repo_url="${HOTFIX_TEST_MADISON_REPO_URL:-https://repo.libreqos.com}"
|
||||
madison_dist_component="${HOTFIX_TEST_MADISON_DIST_COMPONENT:-noble/main}"
|
||||
|
||||
case ",${HOTFIX_TEST_INCONSISTENT_PACKAGES:-}," in
|
||||
*,"$package",*) version="${HOTFIX_TEST_OTHER_VERSION:-255.4-1ubuntu9999+libreqos2}" ;;
|
||||
@@ -90,6 +93,11 @@ class SystemdHotfixScriptTest(unittest.TestCase):
|
||||
version="(none)"
|
||||
fi
|
||||
|
||||
if [ "$command" = "madison" ]; then
|
||||
printf ' %s | %s | %s %s amd64 Packages\\n' "$package" "$version" "$madison_repo_url" "$madison_dist_component"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat <<POLICY
|
||||
$package:
|
||||
Installed: 255.4-1ubuntu8.16
|
||||
@@ -161,6 +169,29 @@ class SystemdHotfixScriptTest(unittest.TestCase):
|
||||
self.assertNotIn("libnss-resolve=", apt_log)
|
||||
self.assertIn("package_version=255.4-1ubuntu9999+libreqos1", marker)
|
||||
|
||||
def test_auto_accepts_policy_repo_url_with_trailing_slash(self):
|
||||
result, apt_log, marker = self.run_install(
|
||||
{
|
||||
"HOTFIX_TEST_REPO_LINE": (
|
||||
" 500 https://repo.libreqos.com/ noble/main amd64 Packages"
|
||||
),
|
||||
"HOTFIX_TEST_MADISON_REPO_URL": "https://example.invalid",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("systemd=255.4-1ubuntu9999+libreqos1", apt_log)
|
||||
self.assertIn("package_version=255.4-1ubuntu9999+libreqos1", marker)
|
||||
|
||||
def test_auto_uses_madison_when_policy_source_line_is_unrecognized(self):
|
||||
result, apt_log, marker = self.run_install(
|
||||
{"HOTFIX_TEST_REPO_LINE": " release o=LibreQoS,l=LibreQoS,n=noble"}
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("systemd=255.4-1ubuntu9999+libreqos1", apt_log)
|
||||
self.assertIn("package_version=255.4-1ubuntu9999+libreqos1", marker)
|
||||
|
||||
def test_auto_rejects_ubuntu_candidate(self):
|
||||
result, _, _ = self.run_install(
|
||||
{"HOTFIX_TEST_CANDIDATE_VERSION": "255.4-1ubuntu8.16"}
|
||||
@@ -198,7 +229,8 @@ class SystemdHotfixScriptTest(unittest.TestCase):
|
||||
{
|
||||
"HOTFIX_TEST_REPO_LINE": (
|
||||
" 500 https://example.invalid noble/main amd64 Packages"
|
||||
)
|
||||
),
|
||||
"HOTFIX_TEST_MADISON_REPO_URL": "https://example.invalid",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -210,7 +242,8 @@ class SystemdHotfixScriptTest(unittest.TestCase):
|
||||
{
|
||||
"HOTFIX_TEST_REPO_LINE": (
|
||||
" 500 https://repo.libreqos.com.evil noble/main amd64 Packages"
|
||||
)
|
||||
),
|
||||
"HOTFIX_TEST_MADISON_REPO_URL": "https://repo.libreqos.com.evil",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user