Fill VSP server field with Proxmox VM CT ID data
This commit is contained in:
@@ -9,7 +9,11 @@
|
|||||||
""",
|
""",
|
||||||
'author': 'Your Company',
|
'author': 'Your Company',
|
||||||
'website': 'https://www.yourcompany.com',
|
'website': 'https://www.yourcompany.com',
|
||||||
'depends': ['base', 'portal'],
|
'depends': [
|
||||||
|
'base',
|
||||||
|
'portal',
|
||||||
|
'mail',
|
||||||
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'views/vps_server_views.xml',
|
'views/vps_server_views.xml',
|
||||||
|
|||||||
Binary file not shown.
@@ -1,19 +1,159 @@
|
|||||||
from odoo import models, fields, api
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
class VPSServer(models.Model):
|
class VPSServer(models.Model):
|
||||||
_name = 'vps.server'
|
_name = 'vps.server'
|
||||||
_description = 'VPS Server'
|
_description = 'VPS Server'
|
||||||
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
|
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
|
||||||
|
|
||||||
name = fields.Char(string='Server Name', required=True)
|
name = fields.Char(string='Server Name', required=True, tracking=True)
|
||||||
ip_address = fields.Char(string='IP Address', required=True)
|
ipv4_address = fields.Text(string='IPv4 Addresses', tracking=True)
|
||||||
cpu = fields.Integer(string='CPU Cores')
|
ipv6_address = fields.Text(string='IPv6 Addresses', tracking=True)
|
||||||
ram = fields.Float(string='RAM (GB)')
|
cpu = fields.Integer(string='CPU Cores', tracking=True)
|
||||||
storage = fields.Float(string='Storage (GB)')
|
ram = fields.Float(string='RAM (GB)', tracking=True)
|
||||||
os = fields.Char(string='Operating System')
|
storage = fields.Float(string='Storage (GB)', tracking=True)
|
||||||
customer_id = fields.Many2one('res.partner', string='Customer', required=True)
|
os = fields.Char(string='Operating System', tracking=True)
|
||||||
|
customer_id = fields.Many2one('res.partner', string='Customer', required=True, tracking=True)
|
||||||
|
proxmox_server_id = fields.Many2one('proxmox.server', string='Proxmox Server', tracking=True)
|
||||||
|
ct_id = fields.Integer(string='CT ID (LXC)', tracking=True)
|
||||||
|
vm_id = fields.Integer(string='VM ID (QEMU)', tracking=True)
|
||||||
|
|
||||||
def _compute_access_url(self):
|
def _compute_access_url(self):
|
||||||
super()._compute_access_url()
|
super()._compute_access_url()
|
||||||
for server in self:
|
for server in self:
|
||||||
server.access_url = '/my/vps-servers/%s' % server.id
|
server.access_url = '/my/vps-servers/%s' % server.id
|
||||||
|
|
||||||
|
@api.onchange('ct_id', 'vm_id')
|
||||||
|
def _onchange_id(self):
|
||||||
|
if self.ct_id and self.vm_id:
|
||||||
|
raise UserError(_("Please provide either CT ID or VM ID, not both."))
|
||||||
|
|
||||||
|
def action_fetch_proxmox_data(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not self.proxmox_server_id:
|
||||||
|
raise UserError(_("Please select a Proxmox server first."))
|
||||||
|
|
||||||
|
if not self.ct_id and not self.vm_id:
|
||||||
|
raise UserError(_("Please provide either CT ID or VM ID."))
|
||||||
|
|
||||||
|
proxmox = self.proxmox_server_id
|
||||||
|
base_url = proxmox.url.rstrip('/')
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"PVEAPIToken={proxmox.api_token_id}={proxmox.api_token_secret}"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# Determine if it's a container or VM
|
||||||
|
if self.ct_id:
|
||||||
|
vm_type = 'lxc'
|
||||||
|
vm_id = self.ct_id
|
||||||
|
else:
|
||||||
|
vm_type = 'qemu'
|
||||||
|
vm_id = self.vm_id
|
||||||
|
|
||||||
|
# Fetch VM/CT status
|
||||||
|
status_response = requests.get(
|
||||||
|
f"{base_url}/api2/json/nodes/pve/{vm_type}/{vm_id}/status/current",
|
||||||
|
verify=False,
|
||||||
|
headers=headers
|
||||||
|
)
|
||||||
|
status_response.raise_for_status()
|
||||||
|
status_data = status_response.json()['data']
|
||||||
|
|
||||||
|
# Fetch VM/CT config
|
||||||
|
config_response = requests.get(
|
||||||
|
f"{base_url}/api2/json/nodes/pve/{vm_type}/{vm_id}/config",
|
||||||
|
verify=False,
|
||||||
|
headers=headers
|
||||||
|
)
|
||||||
|
config_response.raise_for_status()
|
||||||
|
config_data = config_response.json()['data']
|
||||||
|
|
||||||
|
# Update fields
|
||||||
|
self.name = config_data.get('name', f"{vm_type}-{vm_id}")
|
||||||
|
self.cpu = status_data.get('cpus', 0)
|
||||||
|
self.ram = status_data.get('maxmem', 0) / (1024 * 1024 * 1024) # Convert to GB
|
||||||
|
self.storage = status_data.get('maxdisk', 0) / (1024 * 1024 * 1024) # Convert to GB
|
||||||
|
|
||||||
|
# Try to get IP addresses
|
||||||
|
ipv4_addresses = []
|
||||||
|
ipv6_addresses = []
|
||||||
|
|
||||||
|
def parse_ip(ip):
|
||||||
|
try:
|
||||||
|
ip_obj = ipaddress.ip_address(ip.split('/')[0])
|
||||||
|
return str(ip_obj), 'v4' if ip_obj.version == 4 else 'v6'
|
||||||
|
except ValueError:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
if vm_type == 'lxc':
|
||||||
|
for key, value in config_data.items():
|
||||||
|
if key.startswith('net'):
|
||||||
|
ip_configs = value.split(',')
|
||||||
|
for config in ip_configs:
|
||||||
|
if config.startswith('ip=') or config.startswith('ip6='):
|
||||||
|
ip = config.split('=')[1]
|
||||||
|
parsed_ip, ip_type = parse_ip(ip)
|
||||||
|
if parsed_ip:
|
||||||
|
if ip_type == 'v4':
|
||||||
|
ipv4_addresses.append(parsed_ip)
|
||||||
|
else:
|
||||||
|
ipv6_addresses.append(parsed_ip)
|
||||||
|
else: # QEMU
|
||||||
|
for key, value in config_data.items():
|
||||||
|
if key.startswith('ipconfig'):
|
||||||
|
ips = value.split(',')
|
||||||
|
for ip_config in ips:
|
||||||
|
if '=' in ip_config:
|
||||||
|
ip = ip_config.split('=')[1]
|
||||||
|
parsed_ip, ip_type = parse_ip(ip)
|
||||||
|
if parsed_ip:
|
||||||
|
if ip_type == 'v4':
|
||||||
|
ipv4_addresses.append(parsed_ip)
|
||||||
|
else:
|
||||||
|
ipv6_addresses.append(parsed_ip)
|
||||||
|
|
||||||
|
self.ipv4_address = ', '.join(ipv4_addresses) if ipv4_addresses else 'No IPv4 found'
|
||||||
|
self.ipv6_address = ', '.join(ipv6_addresses) if ipv6_addresses else 'No IPv6 found'
|
||||||
|
|
||||||
|
# Try to get OS info
|
||||||
|
if vm_type == 'lxc':
|
||||||
|
self.os = config_data.get('ostype', 'Unknown')
|
||||||
|
else: # QEMU
|
||||||
|
self.os = config_data.get('ostype', 'Unknown')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': _('Data Fetched'),
|
||||||
|
'message': _('Successfully fetched data from Proxmox server.'),
|
||||||
|
'sticky': False,
|
||||||
|
'type': 'success',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise UserError(_("Failed to fetch data from Proxmox server: %s") % str(e))
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
record = super(VPSServer, self).create(vals)
|
||||||
|
record.message_subscribe(partner_ids=[record.customer_id.id])
|
||||||
|
return record
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
res = super(VPSServer, self).write(vals)
|
||||||
|
if 'customer_id' in vals:
|
||||||
|
self.message_subscribe(partner_ids=[vals['customer_id']])
|
||||||
|
return res
|
||||||
|
|
||||||
|
def unlink(self):
|
||||||
|
for record in self:
|
||||||
|
record.message_unsubscribe(partner_ids=[record.customer_id.id])
|
||||||
|
return super(VPSServer, self).unlink()
|
||||||
@@ -4,15 +4,15 @@
|
|||||||
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
|
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
|
||||||
<t t-call="portal.portal_docs_entry">
|
<t t-call="portal.portal_docs_entry">
|
||||||
<t t-set="title">VPS Servers</t>
|
<t t-set="title">VPS Servers</t>
|
||||||
<t t-set="url" t-value="'/my/vps-servers'" />
|
<t t-set="url" t-value="'/my/vps-servers'"/>
|
||||||
<t t-set="placeholder_count" t-value="'vps_server_count'" />
|
<t t-set="placeholder_count" t-value="'vps_server_count'"/>
|
||||||
</t>
|
</t>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="portal_my_vps_servers" name="My VPS Servers">
|
<template id="portal_my_vps_servers" name="My VPS Servers">
|
||||||
<t t-call="portal.portal_layout">
|
<t t-call="portal.portal_layout">
|
||||||
<t t-set="breadcrumbs_searchbar" t-value="True" />
|
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||||
<t t-call="portal.portal_searchbar">
|
<t t-call="portal.portal_searchbar">
|
||||||
<t t-set="title">VPS Servers</t>
|
<t t-set="title">VPS Servers</t>
|
||||||
</t>
|
</t>
|
||||||
@@ -20,9 +20,10 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="active">
|
<tr class="active">
|
||||||
<th>Server Name</th>
|
<th>Server Name</th>
|
||||||
<th class='d-none d-md-table-cell'>IP Address</th>
|
<th class='d-none d-md-table-cell'>IPv4 Address</th>
|
||||||
<th class='d-none d-md-table-cell'>CPU</th>
|
<th class='d-none d-md-table-cell'>IPv6 Address</th>
|
||||||
<th class='text-right'>RAM</th>
|
<th class='text-right'>CPU</th>
|
||||||
|
<th class='text-right'>RAM (GB)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -32,17 +33,20 @@
|
|||||||
<a t-att-href="server.get_portal_url()"
|
<a t-att-href="server.get_portal_url()"
|
||||||
t-attf-class="tr_vps_server_link"
|
t-attf-class="tr_vps_server_link"
|
||||||
t-att-title="server.name">
|
t-att-title="server.name">
|
||||||
<t t-esc="server.name" />
|
<t t-esc="server.name"/>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="d-none d-md-table-cell">
|
<td class="d-none d-md-table-cell">
|
||||||
<span t-field="server.ip_address" />
|
<span t-field="server.ipv4_address"/>
|
||||||
</td>
|
</td>
|
||||||
<td class="d-none d-md-table-cell">
|
<td class="d-none d-md-table-cell">
|
||||||
<span t-field="server.cpu" />
|
<span t-field="server.ipv6_address"/>
|
||||||
</td>
|
</td>
|
||||||
<td class='text-right'>
|
<td class='text-right'>
|
||||||
<span t-field="server.ram" />
|
<span t-field="server.cpu"/>
|
||||||
|
</td>
|
||||||
|
<td class='text-right'>
|
||||||
|
<span t-field="server.ram"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</t>
|
</t>
|
||||||
@@ -68,7 +72,10 @@
|
|||||||
<t t-set="card_body">
|
<t t-set="card_body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
<strong>IP Address:</strong> <span t-field="vps_server.ip_address"/>
|
<strong>IPv4 Address:</strong> <span t-field="vps_server.ipv4_address"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<strong>IPv6 Address:</strong> <span t-field="vps_server.ipv6_address"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
<strong>CPU:</strong> <span t-field="vps_server.cpu"/> cores
|
<strong>CPU:</strong> <span t-field="vps_server.cpu"/> cores
|
||||||
|
|||||||
@@ -5,17 +5,33 @@
|
|||||||
<field name="model">vps.server</field>
|
<field name="model">vps.server</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
|
<header>
|
||||||
|
<button name="action_fetch_proxmox_data" string="Fetch Proxmox Data" type="object" class="oe_highlight"/>
|
||||||
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="ip_address"/>
|
<field name="ipv4_address"/>
|
||||||
|
<field name="ipv6_address"/>
|
||||||
<field name="cpu"/>
|
<field name="cpu"/>
|
||||||
<field name="ram"/>
|
<field name="ram"/>
|
||||||
<field name="storage"/>
|
<field name="storage"/>
|
||||||
<field name="os"/>
|
<field name="os"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
<field name="customer_id"/>
|
<field name="customer_id"/>
|
||||||
|
<field name="proxmox_server_id"/>
|
||||||
|
<field name="ct_id"/>
|
||||||
|
<field name="vm_id"/>
|
||||||
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
<div class="oe_chatter">
|
||||||
|
<field name="message_follower_ids" widget="mail_followers"/>
|
||||||
|
<field name="activity_ids" widget="mail_activity"/>
|
||||||
|
<field name="message_ids" widget="mail_thread"/>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -33,8 +49,12 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="ip_address"/>
|
<field name="ipv4_address"/>
|
||||||
|
<field name="ipv6_address"/>
|
||||||
<field name="customer_id"/>
|
<field name="customer_id"/>
|
||||||
|
<field name="proxmox_server_id"/>
|
||||||
|
<field name="ct_id"/>
|
||||||
|
<field name="vm_id"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
Reference in New Issue
Block a user