Added ability to set IP's of workload hosts and added a filter to ensure the IP addresses are set
This commit is contained in:
@@ -8,7 +8,7 @@ from app.models.network import Network, NetworkPort
|
||||
from datetime import datetime
|
||||
from app.controller import api_bp
|
||||
import uuid
|
||||
from app.scheduling_filters import ExcludeAllDisabledHosts, MostAvailableCapacity, ExcludeAllOfflineHosts, DockerCapableHosts
|
||||
from app.scheduling_filters import ExcludeAllDisabledHosts, MostAvailableCapacity, ExcludeAllOfflineHosts, DockerCapableHosts, ExcludeHostsWithoutNorthSouthIP
|
||||
from app.services.cloudflare import CloudflareTunnelManager
|
||||
from werkzeug.exceptions import abort
|
||||
from sqlalchemy import or_
|
||||
@@ -168,7 +168,7 @@ def add_container_workload():
|
||||
all_workload_hosts = WorkloadHost.query.filter_by(region_id=request_vdc.region.id).all()
|
||||
logger.debug(f"Found {len(all_workload_hosts)} hosts eligible for placement")
|
||||
|
||||
filters = [DockerCapableHosts(), ExcludeAllOfflineHosts(), ExcludeAllDisabledHosts(), MostAvailableCapacity()]
|
||||
filters = [ExcludeHostsWithoutNorthSouthIP(), DockerCapableHosts(), ExcludeAllOfflineHosts(), ExcludeAllDisabledHosts(), MostAvailableCapacity()]
|
||||
filtered_hosts = all_workload_hosts
|
||||
for filter in filters:
|
||||
filtered_hosts = filter.apply(filtered_hosts)
|
||||
@@ -252,7 +252,7 @@ def add_container_workload():
|
||||
selected_host.id, preferred_port=preferred_external_port
|
||||
)
|
||||
|
||||
logger.info(f"Assigning internal {internal_port} -> external {assigned_external_port} for container {new_container.id}")
|
||||
logger.info(f"Assigning internal port:{internal_port} -> external port:{assigned_external_port} for container {new_container.id}")
|
||||
|
||||
# Add this port to NSController launch params
|
||||
nscontroller_launch_params['ports'].append({
|
||||
@@ -662,10 +662,12 @@ def get_container_workloads():
|
||||
|
||||
@api_bp.route('/workloads/pods', methods=['GET'])
|
||||
def get_pods():
|
||||
pods = ContainerPod.query.all()
|
||||
# pods = ContainerPod.query.all()
|
||||
active_pods = ContainerPod.query_with_only_active_containers().all()
|
||||
|
||||
response = []
|
||||
|
||||
for pod in pods:
|
||||
for pod in active_pods:
|
||||
# Get associated tunnel (if exists)
|
||||
tunnel = CloudflareTunnel.query.filter_by(
|
||||
nscontroller_workload_id=pod.nscontroller_workload_id,
|
||||
|
||||
+15
-3
@@ -226,8 +226,8 @@ class WorkloadHost(BaseModel):
|
||||
heartbeat_status = Column(String(50), nullable=True)
|
||||
last_heartbeat_time = Column(DateTime, nullable=True)
|
||||
ip_address_all = Column(LONGTEXT, nullable=True)
|
||||
ip_address_eastwest = Column(String(25), nullable=True)
|
||||
ip_address_northsouth = Column(String(25), nullable=True)
|
||||
ip_address_eastwest = Column(String(25), nullable=True) #This IP is used for SDN capabilities in a region
|
||||
ip_address_northsouth = Column(String(25), nullable=True) #This IP is used for port forwarding into a host for containers primarily
|
||||
|
||||
workloads = relationship(
|
||||
"Workload",
|
||||
@@ -686,7 +686,19 @@ class ContainerPod(BaseModel):
|
||||
nscontroller_workload = relationship("Workload", foreign_keys=[nscontroller_workload_id])
|
||||
container_mappings = relationship("ContainerPodContainer", back_populates="pod", cascade="all, delete-orphan")
|
||||
vdc = relationship("VirtualDataCenter")
|
||||
|
||||
|
||||
@classmethod
|
||||
def query_with_only_active_containers(cls):
|
||||
"""
|
||||
Returns ContainerPods where all mapped containers are not soft-deleted.
|
||||
"""
|
||||
return (
|
||||
db.session.query(cls)
|
||||
.join(ContainerPodContainer, cls.id == ContainerPodContainer.pod_id)
|
||||
.join(Workload, Workload.id == ContainerPodContainer.container_workload_id)
|
||||
.filter(Workload.deleted == False)
|
||||
.distinct()
|
||||
)
|
||||
class Image(BaseModel):
|
||||
__tablename__ = "images"
|
||||
location = Column(String(255), nullable=False)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from app.models.models import WorkloadHost
|
||||
from logger import logger
|
||||
|
||||
import ipaddress
|
||||
class SchedulingFilter(ABC):
|
||||
@abstractmethod
|
||||
def apply(self, hosts):
|
||||
@@ -113,4 +113,60 @@ class MostAvailableCapacity(SchedulingFilter):
|
||||
f"Input: {len(hosts)}, Output: {len(filtered_hosts)}\n"
|
||||
f"Priority order: {sorted_ids}"
|
||||
)
|
||||
return filtered_hosts
|
||||
return filtered_hosts
|
||||
|
||||
|
||||
|
||||
class ExcludeHostsWithoutNorthSouthIP(SchedulingFilter):
|
||||
"""
|
||||
Filter that removes every WorkloadHost lacking a valid north-south IP address.
|
||||
|
||||
A host is **kept** only if:
|
||||
• `ip_address_northsouth` is not None / empty, **and**
|
||||
• its value parses as a valid IPv4 or IPv6 address.
|
||||
|
||||
All others are filtered out.
|
||||
"""
|
||||
|
||||
def apply(self, hosts):
|
||||
"""
|
||||
Iterate over *hosts* and return only those with a valid
|
||||
`ip_address_northsouth`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
hosts : list[WorkloadHost]
|
||||
Candidate hosts.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[WorkloadHost]
|
||||
Hosts that passed the IP-validation check.
|
||||
"""
|
||||
host_ids = [str(h.id) for h in hosts]
|
||||
logger.debug(
|
||||
f"Starting ExcludeHostsWithoutNorthSouthIP filter. "
|
||||
f"Input hosts ({len(hosts)}): {host_ids}"
|
||||
)
|
||||
|
||||
filtered_hosts, removed_ids = [], []
|
||||
for host in hosts:
|
||||
ip_addr = getattr(host, "ip_address_northsouth", None)
|
||||
|
||||
try:
|
||||
# ipaddress throws ValueError on bad input
|
||||
if ip_addr and ipaddress.ip_address(ip_addr):
|
||||
filtered_hosts.append(host)
|
||||
else:
|
||||
removed_ids.append(str(host.id))
|
||||
except ValueError:
|
||||
removed_ids.append(str(host.id))
|
||||
|
||||
filtered_ids = [str(h.id) for h in filtered_hosts]
|
||||
logger.debug(
|
||||
f"Finished ExcludeHostsWithoutNorthSouthIP. "
|
||||
f"Input: {len(hosts)}, Filtered: {len(filtered_hosts)}\n"
|
||||
f"Removed hosts: {removed_ids}\n"
|
||||
f"Remaining hosts: {filtered_ids}"
|
||||
)
|
||||
return filtered_hosts
|
||||
|
||||
@@ -142,6 +142,27 @@ def render_detail():
|
||||
st.markdown(f"**Available for Scheduling:** `{workload_host['available_for_scheduling']}`")
|
||||
st.markdown(f"**Placement Priority:** `{workload_host['placement_priority']}`")
|
||||
st.markdown(f"**Status:** `{workload_host['status']}`")
|
||||
# Network configuration section
|
||||
st.subheader("Network Configuration")
|
||||
|
||||
# Input fields for editing East-West and North-South IPs
|
||||
current_ew_ip = workload_host.get('ip_address_eastwest') or ""
|
||||
current_ns_ip = workload_host.get('ip_address_northsouth') or ""
|
||||
|
||||
new_ew_ip = st.text_input("East-West IP Address", value=current_ew_ip)
|
||||
new_ns_ip = st.text_input("North-South IP Address", value=current_ns_ip)
|
||||
|
||||
if st.button("Save Network Configuration"):
|
||||
update_payload = {
|
||||
"ip_address_eastwest": new_ew_ip,
|
||||
"ip_address_northsouth": new_ns_ip
|
||||
}
|
||||
update_result = st.session_state.client.update_workload_host(host_id, update_payload)
|
||||
if update_result and update_result.get('success'):
|
||||
st.success("Network configuration updated.")
|
||||
st.rerun()
|
||||
else:
|
||||
st.error("Failed to update network configuration.")
|
||||
|
||||
# Display OVS Bridges
|
||||
st.subheader("Attached OVS Bridges")
|
||||
@@ -152,7 +173,6 @@ def render_detail():
|
||||
else:
|
||||
st.info("No OVS bridges attached to this host.")
|
||||
|
||||
# ... (rest of the existing detail view code remains the same)
|
||||
|
||||
if st.session_state.view_type == 'detail' and st.session_state.selected_resource_type == 'workload_host':
|
||||
render_detail()
|
||||
|
||||
Reference in New Issue
Block a user