ssh туннель для БД
This commit is contained in:
parent
67f440682f
commit
317b701afe
2 changed files with 63 additions and 7 deletions
|
|
@ -710,11 +710,48 @@ async def discover_via_ssh(
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# Step 0.5: upsert DB server as a device now (before connection attempt),
|
||||||
|
# so the user can see it and configure tunnel even if connection fails.
|
||||||
|
now_pre = datetime.now(timezone.utc)
|
||||||
|
db_device_obj: Device | None = None
|
||||||
|
if db_host and db_host != obj.server_ip:
|
||||||
|
try:
|
||||||
|
db_device_obj = (await db.execute(
|
||||||
|
select(Device).where(Device.object_id == obj.id, Device.ip == db_host)
|
||||||
|
)).scalar_one_or_none()
|
||||||
|
if db_device_obj is None:
|
||||||
|
db_device_obj = Device(
|
||||||
|
object_id=obj.id,
|
||||||
|
ip=db_host,
|
||||||
|
hostname="db_server",
|
||||||
|
category="vm",
|
||||||
|
role="other",
|
||||||
|
source="auto_ssh",
|
||||||
|
location="server_room",
|
||||||
|
status="unknown",
|
||||||
|
)
|
||||||
|
db.add(db_device_obj)
|
||||||
|
await db.flush()
|
||||||
|
logger.info("[ssh_discovery] %s (db_server) - PRE-ADDED before connection attempt", db_host)
|
||||||
|
result.created += 1
|
||||||
|
result.actions.append(DeviceAction(
|
||||||
|
ip=db_host, hostname="db_server", action="ADDED",
|
||||||
|
reason="object infrastructure device (vm/db_server)", category="vm", role="other",
|
||||||
|
))
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("[ssh_discovery] Failed to pre-upsert db_server device %s: %s", db_host, exc)
|
||||||
|
|
||||||
|
# Read tunnel flag from the device (user may have set it via UI), fall back to object setting
|
||||||
|
device_via_tunnel = bool(
|
||||||
|
(db_device_obj.connection_params or {}).get("db_via_tunnel")
|
||||||
|
) if db_device_obj else False
|
||||||
|
use_tunnel = device_via_tunnel or obj.db_via_tunnel
|
||||||
|
|
||||||
# Step 1: get rack hosts from object's DB
|
# Step 1: get rack hosts from object's DB
|
||||||
await _emit("disc_action", {"message": "Получение списка стоек из БД..."})
|
await _emit("disc_action", {"message": "Получение списка стоек из БД..."})
|
||||||
logger.info(
|
logger.info(
|
||||||
"[ssh_discovery] Connecting to object DB: host=%s port=%s dbname=%s user=%s table=%s via_tunnel=%s",
|
"[ssh_discovery] Connecting to object DB: host=%s port=%s dbname=%s user=%s table=%s via_tunnel=%s",
|
||||||
db_host, db_port, db_name, db_user, db_table, obj.db_via_tunnel,
|
db_host, db_port, db_name, db_user, db_table, use_tunnel,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
rack_hosts = await _get_rack_hosts_direct(
|
rack_hosts = await _get_rack_hosts_direct(
|
||||||
|
|
@ -724,14 +761,24 @@ async def discover_via_ssh(
|
||||||
password=db_password or "",
|
password=db_password or "",
|
||||||
dbname=db_name,
|
dbname=db_name,
|
||||||
table=db_table,
|
table=db_table,
|
||||||
via_tunnel=obj.db_via_tunnel,
|
via_tunnel=use_tunnel,
|
||||||
tunnel_host=obj.server_ip,
|
tunnel_host=obj.server_ip,
|
||||||
tunnel_ssh_port=ssh_port,
|
tunnel_ssh_port=ssh_port,
|
||||||
tunnel_ssh_user=obj.ssh_user,
|
tunnel_ssh_user=obj.ssh_user,
|
||||||
tunnel_ssh_password=ssh_password,
|
tunnel_ssh_password=ssh_password,
|
||||||
)
|
)
|
||||||
|
# Connection succeeded — mark DB device online
|
||||||
|
if db_device_obj is not None:
|
||||||
|
db_device_obj.status = "online"
|
||||||
|
db_device_obj.last_seen = now_pre
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
result.errors.append(f"Cannot fetch rack hosts from object DB: {exc}")
|
# Mark DB device offline so the user sees it's unreachable
|
||||||
|
if db_device_obj is not None:
|
||||||
|
db_device_obj.status = "offline"
|
||||||
|
err_msg = f"Cannot fetch rack hosts from object DB: {exc}"
|
||||||
|
if "timeout" in str(exc).lower():
|
||||||
|
err_msg += ". Попробуйте настроить SSH-туннель (отредактируйте устройство БД и включите опцию)"
|
||||||
|
result.errors.append(err_msg)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if not rack_hosts:
|
if not rack_hosts:
|
||||||
|
|
@ -774,14 +821,12 @@ async def discover_via_ssh(
|
||||||
rack_id_mapping[ext_id] = new_rack.id
|
rack_id_mapping[ext_id] = new_rack.id
|
||||||
logger.info(f"[ssh_discovery] Created rack entry '{rack_name}' rack_type={rack_type_db} (id={new_rack.id})")
|
logger.info(f"[ssh_discovery] Created rack entry '{rack_name}' rack_type={rack_type_db} (id={new_rack.id})")
|
||||||
|
|
||||||
# Step 1b: Add main CAPS server and DB server as devices
|
# Step 1b: Add main CAPS server as device (DB server was already upserted in Step 0.5)
|
||||||
# verified=True means we successfully connected to this host during this discovery run
|
# verified=True means we successfully connected to this host during this discovery run
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
infra_devices = []
|
infra_devices = []
|
||||||
if obj.server_ip:
|
if obj.server_ip:
|
||||||
infra_devices.append((obj.server_ip, "main_server", "caps_server", server_ip_ssh_verified))
|
infra_devices.append((obj.server_ip, "main_server", "caps_server", server_ip_ssh_verified))
|
||||||
if db_host and db_host != obj.server_ip:
|
|
||||||
infra_devices.append((db_host, "vm", "db_server", True)) # DB query succeeded = online
|
|
||||||
|
|
||||||
for infra_ip, infra_category, infra_hostname, infra_verified in infra_devices:
|
for infra_ip, infra_category, infra_hostname, infra_verified in infra_devices:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ export function DeviceEditModal({ open, device, objId, onClose, onSaved }: Props
|
||||||
ssh_user_override: device.ssh_user_override ?? '',
|
ssh_user_override: device.ssh_user_override ?? '',
|
||||||
ssh_port_override: device.ssh_port_override ?? undefined,
|
ssh_port_override: device.ssh_port_override ?? undefined,
|
||||||
ssh_key_id_override: device.ssh_key_id_override ?? undefined,
|
ssh_key_id_override: device.ssh_key_id_override ?? undefined,
|
||||||
|
db_via_tunnel: !!(device.connection_params?.db_via_tunnel),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
form.resetFields()
|
form.resetFields()
|
||||||
|
|
@ -86,10 +87,14 @@ export function DeviceEditModal({ open, device, objId, onClose, onSaved }: Props
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const values = await form.validateFields()
|
const values = await form.validateFields()
|
||||||
const payload = stripEmpty(values)
|
const { db_via_tunnel, ...rest } = values
|
||||||
|
const payload = stripEmpty(rest)
|
||||||
if ('rack_id' in values && values.rack_id === undefined) {
|
if ('rack_id' in values && values.rack_id === undefined) {
|
||||||
payload.rack_id = null
|
payload.rack_id = null
|
||||||
}
|
}
|
||||||
|
// Merge db_via_tunnel into connection_params
|
||||||
|
const existingParams = device?.connection_params ?? {}
|
||||||
|
payload.connection_params = { ...existingParams, db_via_tunnel: !!db_via_tunnel }
|
||||||
try {
|
try {
|
||||||
let saved: DeviceItem
|
let saved: DeviceItem
|
||||||
if (device) {
|
if (device) {
|
||||||
|
|
@ -213,6 +218,12 @@ export function DeviceEditModal({ open, device, objId, onClose, onSaved }: Props
|
||||||
options={(sshKeys ?? []).map((k) => ({ value: k.id, label: `${k.name} (${k.path})` }))}
|
options={(sshKeys ?? []).map((k) => ({ value: k.id, label: `${k.name} (${k.path})` }))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Divider orientation="left" plain>Подключение к БД</Divider>
|
||||||
|
|
||||||
|
<Form.Item name="db_via_tunnel" valuePropName="checked">
|
||||||
|
<Checkbox>Подключаться к БД через SSH-туннель (через main_server объекта)</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue