Files
sunhpc-go/pkg/database/schema.go

543 lines
21 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package db defines the database schema.
package database
import (
"database/sql"
"fmt"
"sunhpc/pkg/logger"
)
func BaseTables() map[string]string {
return map[string]string{
"appliances": `
CREATE TABLE IF NOT EXISTS appliances (
ID integer primary key autoincrement,
Name varchar(32) not null default '',
Graph varchar(64) not null default 'default',
Node varchar(64) not null default '',
OS varchar(64) not null default 'linux'
);
`,
"memberships": `
CREATE TABLE IF NOT EXISTS memberships (
ID integer primary key autoincrement,
Name varchar(64) not null default '',
Appliance integer(11) default '0',
Distribution integer(11) default '1',
Public varchar(64) not null default 'no'
);
`,
"categories": `
CREATE TABLE IF NOT EXISTS categories (
ID integer primary key autoincrement,
Name varchar(64) not null unique default '0',
Description varchar(255) default null,
UNIQUE(Name)
);
`,
"catindex": `
CREATE TABLE IF NOT EXISTS catindex (
ID integer primary key autoincrement,
Name varchar(64) not null unique default '0',
Category integer not null,
Foreign key(Category) references categories(ID) on delete cascade
);
`,
"resolvechain": `
CREATE TABLE IF NOT EXISTS resolvechain (
ID integer primary key autoincrement,
Name varchar(64) not null default '0',
Category integer(11) not null,
Precedence integer(11) not null default '10',
UNIQUE(Name, Category)
Foreign key(Category) references categories(ID) on delete cascade
);
`,
"nodes": `
CREATE TABLE IF NOT EXISTS nodes (
ID integer primary key autoincrement,
Name varchar default null,
Membership integer default '2',
CPUs integer not null default '1',
Rack varchar default null,
Rank integer default null,
Arch varchar default null,
OS varchar default null,
RunAction varchar(64) default 'os',
InstallAction varchar(64) default 'install'
);
create index if not exists idx_nodes_name on nodes(Name);
`,
"aliases": `
CREATE TABLE IF NOT EXISTS aliases (
ID integer primary key autoincrement,
Node integer not null default '0',
Name varchar default null,
Foreign key(Node) references nodes(ID) on delete cascade
);
create index if not exists idx_aliases_name on aliases(Name);
`,
"networks": `
CREATE TABLE IF NOT EXISTS networks (
ID integer primary key autoincrement,
Node integer not null default '0',
MAC varchar default null,
IP varchar default null,
Name varchar default null,
Device varchar default null,
Subnet integer default null,
Module varchar default null,
VlanID integer default null,
Options varchar default null,
Channel varchar default null,
Foreign key(Node) references nodes(ID) on delete cascade,
Foreign key(Subnet) references subnets(ID) on delete cascade
);
create index if not exists idx_networks_name on networks(Name);
`,
"globalroutes": `
CREATE TABLE IF NOT EXISTS globalroutes (
Network varchar(32) not null default '',
Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '',
Subnet integer default null,
Primary key(Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade
);
`,
"osroutes": `
CREATE TABLE IF NOT EXISTS osroutes (
OS varchar(64) not null default 'linux',
Network varchar(32) not null default '',
Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '',
Subnet integer default null,
Primary key(OS, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade
);
`,
"applianceroutes": `
CREATE TABLE IF NOT EXISTS applianceroutes (
Appliance varchar(11) not null default '0',
Network varchar(32) not null default '',
Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '',
Subnet integer default null,
Primary key(Appliance, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade
);
`,
"noderoutes": `
CREATE TABLE IF NOT EXISTS noderoutes (
Node varchar(11) not null default '0',
Network varchar(32) not null default '',
Netmask varchar(32) not null default '',
Gateway varchar(32) not null default '',
Subnet integer default null,
Primary key(Node, Network, Netmask)
Foreign key(Subnet) references subnets(ID) on delete cascade
);
`,
"subnets": `
CREATE TABLE IF NOT EXISTS subnets (
ID integer primary key autoincrement,
name varchar(32) unique not null,
dnszone varchar(64) unique not null,
subnet varchar(32) default null,
netmask varchar(32) default null,
mtu integer(11) default '1500',
servedns boolean default false
);
`,
"publickeys": `
CREATE TABLE IF NOT EXISTS publickeys (
ID integer primary key autoincrement,
Node integer(11) not null default '0',
Public_Key varchar(8192) default null,
Description varchar(8192) default null,
Foreign key(Node) references nodes(ID) on delete cascade
);
`,
"secglobal": `
CREATE TABLE IF NOT EXISTS secglobal (
Attr varchar(128) default null,
Value text,
Enc varchar(128) default null,
Primary key(Attr)
);
`,
"secnodes": `
CREATE TABLE IF NOT EXISTS secnodes (
Attr varchar(128) default null,
Enc varchar(128) default null,
Value text,
Node integer(15) not null default '0',
Primary key(Attr, Node)
);
`,
"attributes": `
CREATE TABLE IF NOT EXISTS attributes (
ID integer primary key autoincrement,
Attr varchar(128) not null,
Value text,
Shadow text,
Category integer(11) not null,
Catindex integer(11) not null,
UNIQUE(Attr, Category, Catindex),
Foreign key(Catindex) references catindex(ID) on delete cascade
);
`,
"partitions": `
CREATE TABLE IF NOT EXISTS partitions (
ID integer primary key autoincrement,
Node integer(15) not null default '0',
Device varchar(128) not null default '',
MountPoint varchar(128) not null default '',
SectorStart varchar(128) not null default '',
PartitionSize varchar(128) not null default '',
FsType varchar(128) not null default '',
PartitionFlags varchar(128) not null default '',
FormatFlags varchar(128) not null default ''
);
`,
"firewalls": `
CREATE TABLE IF NOT EXISTS firewalls (
ID integer primary key autoincrement,
Rulename varchar(128) not null,
Rulesrc varchar(256) not null default 'custom',
InSubnet int(11),
OutSubnet int(11),
Service varchar(256),
Protocol varchar(256),
Action varchar(256),
Chain varchar(256),
Flags varchar(256),
Comment varchar(256),
Category integer(11) not null,
Catindex integer(11) not null,
Check(rulesrc IN ('system', 'custom'))
UNIQUE(Rulename, Category, Catindex),
Foreign key(Catindex) references catindex(ID) on delete cascade
);
`,
"rolls": `
CREATE TABLE IF NOT EXISTS rolls (
ID integer primary key autoincrement,
Name varchar(128) not null default '',
Version varchar(32) not null default '',
Arch varchar(32) not null default '',
OS varchar(64) not null default 'linux',
Enabled varchar(3) not null default 'yes',
Check(Enabled IN ('yes', 'no'))
Check(OS IN ('linux', 'other'))
);
`,
"noderolls": `
CREATE TABLE IF NOT EXISTS noderolls (
Node varchar(11) not null default '0',
RollID varchar(11) not null,
Primary key(Node, RollID)
);
`,
"bootactions": `
CREATE TABLE IF NOT EXISTS bootactions (
ID integer primary key autoincrement,
Action varchar(256) default null,
Kernel varchar(256) default null,
Ramdisk varchar(256) default null,
Args varchar(1024) default null
);
`,
"bootflags": `
CREATE TABLE IF NOT EXISTS bootflags (
ID integer primary key autoincrement,
Node integer(11) not null default '0',
Flags varchar(256) default null
);
`,
"distributions": `
CREATE TABLE IF NOT EXISTS distributions (
ID integer primary key autoincrement,
Name varchar(32) not null default '',
OS varchar(32) default '',
Release varchar(32) default ''
);
`,
"vnet": `
DROP VIEW IF EXISTS vnet;
CREATE VIEW vnet AS
SELECT
n.name AS nodename, /* 查询nodes表中name字段,将字段改名为nodename */
m.name AS membership,
a.name AS appliance,
n.rack, n.rank, /* 查询nodes表中rack和rank字段,使用原始字段名 */
s.name AS subnet,
nt.ip, nt.device, nt.module,
nt.name AS hostname,
s.dnszone AS domainname,
s.netmask, s.mtu
FROM
nodes n /* 主表: 先查询nodes表,别名n */
inner join memberships m on n.membership=m.id /* 连接memberships表,on只保留满足条件的行 */
inner join appliances a on m.appliance=a.id /* 连接appliances表,on只保留满足条件的行 */
inner join networks nt on n.id=nt.node /* 连接networks表,on只保留满足条件的行 */
inner join subnets s on nt.subnet=s.id /* 连接subnets表,on只保留满足条件的行 */
;
`,
"hostselections": `
DROP VIEW IF EXISTS hostselections;
CREATE VIEW hostselections AS
SELECT
n.name AS host,
c.id as category,
ci.id as selection
FROM
nodes n
inner join memberships m on n.membership=m.id -- 节点表关联所属分组
inner join appliances a on m.appliance=a.id -- 分组关联所属应用角色
inner join categories c on
-- 匹配4类分层配置的category(全局/OS/应用/主机)
c.name in ('global', 'os', 'appliance', 'host')
inner join catindex ci on
-- 核心匹配逻辑: category和catindex的name字段一一对应
(c.name = 'global' and ci.name = 'global') or
(c.name = 'os' and ci.name = n.os) or
(c.name = 'appliance' and ci.name = a.name) or
(c.name = 'host' and ci.name = n.name)
;
`,
"vcatindex": `
-- 视图vcatindex: 类别索引可读试图
DROP VIEW IF EXISTS vcatindex;
CREATE VIEW vcatindex AS
SELECT
c.id AS ID,
cat.Name AS Category,
ci.Name AS catindex
FROM
categories cat
inner join catindex ci on ci.category=cat.id
;
`,
"vresolvechain": `
-- 视图vresolvechain: 解析链可读试图
DROP VIEW IF EXISTS vresolvechain;
CREATE VIEW vresolvechain AS
SELECT
r.name AS chain,
cat.name AS category,
precedence
FROM
resolvechain r
inner join categories cat on r.category=cat.id
order by chain, precedence
;
`,
"vattributes": `
-- 视图vattributes: 属性可读试图
DROP VIEW IF EXISTS vattributes;
CREATE VIEW vattributes AS
SELECT
a.id,
attr,
value,
shadow,
cat.name AS category,
ci.name AS catindex
FROM
attributes a
inner join catindex ci on a.catindex=ci.id
inner join categories cat on a.category=cat.id
order by attr, catindex, category
;
`,
"vfirewalls": `
-- 视图vfirewalls: 防火墙规则可读试图
DROP VIEW IF EXISTS vfirewalls;
CREATE VIEW vfirewalls AS
SELECT
f.id,
f.Rulename,
f.Rulesrc,
f.InSubnet,
f.OutSubnet,
f.Service,
f.Protocol,
f.Action,
f.Chain,
f.Flags,
f.Comment,
cat.name AS category,
ci.name AS catindex
FROM
firewalls f
inner join catindex ci on f.catindex=ci.id
inner join categories cat on f.category=cat.id
order by f.Rulename, catindex, category
;
`,
"vhostselections": `
-- 视图vhostselections: 主机选择可读试图
DROP VIEW IF EXISTS vhostselections;
CREATE VIEW vhostselections AS
SELECT
hs.host AS host,
cat.name AS category,
ci.name AS selection
FROM
hostselections hs
inner join categories cat on hs.category=cat.id
inner join catindex ci on hs.selection=ci.id
order by host, category, selection
;
`,
"vmapcategoryindex": `
-- 视图vmapcategoryindex: 类别索引映射可读试图
DROP VIEW IF EXISTS vmapcategoryindex;
CREATE VIEW vmapCategoryIndex AS
SELECT
cat.name AS categoryName,
ci.name AS categoryIndex,
ci.ID AS index_ID
FROM
cateindex ci
inner join categories cat on ci.category=cat.id
;
`,
}
}
func InitBaseData(conn *sql.DB) error {
logger.Debug("初始化基础数据...")
// ========== 第一步:插入 categories 数据 ==========
categoryData := []struct {
Name string
Description string
}{
{"global", "Global Defaults"},
{"os", "OS Choice(Linux,Sunos)"},
{"appliance", "Logical Appliances"},
{"rack", "Machine Room Racks"},
{"host", "Hosts - Physical AND Virtual"},
}
// 批量插入 categories (忽略重复)
logger.Debug("插入 categories 数据...")
for _, cd := range categoryData {
query := `
insert or ignore into categories (Name, Description)
values (?, ?)
`
fullSQL := ReplaceSQLQuery(query, cd.Name, cd.Description)
logger.Debugf("执行语句: %s", fullSQL)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, cd.Name, cd.Description)
if err != nil {
return fmt.Errorf("error inserting category %s: %w", cd.Name, err)
}
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
}
// ========== 第二步:插入 catindex 数据 ==========
catindexData := []struct {
Name string
Category string
}{
{"global", "global"},
{"linux", "os"},
{"sunos", "os"},
{"frontend", "appliance"},
{"compute", "appliance"},
{"nas", "appliance"},
{"network", "appliance"},
{"power", "appliance"},
{"devel-server", "appliance"},
{"login", "appliance"},
}
// 批量插入 catindex (忽略重复)
logger.Debug("插入 Catindex 数据...")
for _, ci := range catindexData {
// 动态获取类别ID (复用MapCategory函数)
catID, err := MapCategory(conn, ci.Category)
if err != nil {
return fmt.Errorf("error mapping category %s: %w", ci.Category, err)
}
if catID == 0 {
return fmt.Errorf("category %s not found", ci.Category)
}
// 插入 catindex (忽略重复)
query := `
insert or ignore into catindex (Name, Category)
values (?, ?)
`
fullSQL := ReplaceSQLQuery(query, ci.Name, catID)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, ci.Name, catID)
if err != nil {
return fmt.Errorf("error inserting catindex %s: %w", ci.Name, err)
}
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
}
// ========== 第三步:插入 resolvechain 数据 ==========
resolveChainData := []struct {
Name string // 解析链名称,global/linux/sunos
Category string // 类别名称,linux/sunos
Precedence int // 优先级,数值越大优先级越高
}{
{"default", "global", 10},
{"default", "os", 20},
{"default", "appliance", 30},
{"default", "rack", 40},
{"default", "host", 50},
}
// 批量插入 resolvechain (忽略重复)
logger.Debugf("插入 resolvechain 数据...")
for _, rcd := range resolveChainData {
// 动态获取类别ID (复用MapCategory函数)
catID, err := MapCategory(conn, rcd.Category)
if err != nil {
return fmt.Errorf("error mapping category %s: %w", rcd.Category, err)
}
if catID == 0 {
return fmt.Errorf("category %s not found", rcd.Category)
}
// 插入 resolvechain (忽略重复)
query := `
insert or ignore into resolvechain (Name, Category, Precedence)
values (?, ?, ?)
`
fullSQL := ReplaceSQLQuery(query, rcd.Name, catID, rcd.Precedence)
// 执行 SQL 语句仍用占位符避免SQL注入
result, err := conn.Exec(query, rcd.Name, catID, rcd.Precedence)
if err != nil {
return fmt.Errorf("error inserting resolvechain %s: %w", rcd.Name, err)
}
id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last insert ID: %w", err)
}
logger.Debugf("执行语句: %s, 插入ID: %d", fullSQL, id)
}
return nil
}