Fill VSP server field with Proxmox VM CT ID data
This commit is contained in:
@@ -9,7 +9,11 @@
|
||||
""",
|
||||
'author': 'Your Company',
|
||||
'website': 'https://www.yourcompany.com',
|
||||
'depends': ['base', 'portal'],
|
||||
'depends': [
|
||||
'base',
|
||||
'portal',
|
||||
'mail',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'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):
|
||||
_name = 'vps.server'
|
||||
_description = 'VPS Server'
|
||||
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string='Server Name', required=True)
|
||||
ip_address = fields.Char(string='IP Address', required=True)
|
||||
cpu = fields.Integer(string='CPU Cores')
|
||||
ram = fields.Float(string='RAM (GB)')
|
||||
storage = fields.Float(string='Storage (GB)')
|
||||
os = fields.Char(string='Operating System')
|
||||
customer_id = fields.Many2one('res.partner', string='Customer', required=True)
|
||||
name = fields.Char(string='Server Name', required=True, tracking=True)
|
||||
ipv4_address = fields.Text(string='IPv4 Addresses', tracking=True)
|
||||
ipv6_address = fields.Text(string='IPv6 Addresses', tracking=True)
|
||||
cpu = fields.Integer(string='CPU Cores', tracking=True)
|
||||
ram = fields.Float(string='RAM (GB)', tracking=True)
|
||||
storage = fields.Float(string='Storage (GB)', tracking=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):
|
||||
super()._compute_access_url()
|
||||
for server in self:
|
||||
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()
|
||||
@@ -20,9 +20,10 @@
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Server Name</th>
|
||||
<th class='d-none d-md-table-cell'>IP Address</th>
|
||||
<th class='d-none d-md-table-cell'>CPU</th>
|
||||
<th class='text-right'>RAM</th>
|
||||
<th class='d-none d-md-table-cell'>IPv4 Address</th>
|
||||
<th class='d-none d-md-table-cell'>IPv6 Address</th>
|
||||
<th class='text-right'>CPU</th>
|
||||
<th class='text-right'>RAM (GB)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -36,9 +37,12 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<span t-field="server.ip_address" />
|
||||
<span t-field="server.ipv4_address"/>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<span t-field="server.ipv6_address"/>
|
||||
</td>
|
||||
<td class='text-right'>
|
||||
<span t-field="server.cpu"/>
|
||||
</td>
|
||||
<td class='text-right'>
|
||||
@@ -68,7 +72,10 @@
|
||||
<t t-set="card_body">
|
||||
<div class="row">
|
||||
<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 class="col-12 col-md-6">
|
||||
<strong>CPU:</strong> <span t-field="vps_server.cpu"/> cores
|
||||
|
||||
@@ -5,17 +5,33 @@
|
||||
<field name="model">vps.server</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button name="action_fetch_proxmox_data" string="Fetch Proxmox Data" type="object" class="oe_highlight"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="ip_address"/>
|
||||
<field name="ipv4_address"/>
|
||||
<field name="ipv6_address"/>
|
||||
<field name="cpu"/>
|
||||
<field name="ram"/>
|
||||
<field name="storage"/>
|
||||
<field name="os"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="customer_id"/>
|
||||
<field name="proxmox_server_id"/>
|
||||
<field name="ct_id"/>
|
||||
<field name="vm_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</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>
|
||||
</field>
|
||||
</record>
|
||||
@@ -33,8 +49,12 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="ip_address"/>
|
||||
<field name="ipv4_address"/>
|
||||
<field name="ipv6_address"/>
|
||||
<field name="customer_id"/>
|
||||
<field name="proxmox_server_id"/>
|
||||
<field name="ct_id"/>
|
||||
<field name="vm_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user