package db import ( "fmt" "net" "os" "strings" "sync" ) /* // 获取数据库实例 database, err := db.GetInstance() if err != nil { log.Fatal(err) } defer database.Close() // 创建Helper helper, _ := db.NewDBHelper() // 执行查询 helper.Execute("SELECT * FROM nodes WHERE rack = ?", 1) // 获取一行 row, _ := helper.FetchOne() if row != nil { log.Infof("节点: %v", row["name"]) } // 获取所有行 rows, _ := helper.FetchAll() log.Infof("共 %d 个节点", len(rows)) // 使用Helper方法 hostname, _ := helper.GetHostname("192.168.1.1") log.Infof("解析主机名: %s", hostname) // 设置属性 helper.SetCategoryAttr("global", "global", "Kickstart_PrivateHostname", "sunhpc-master") // 获取属性 val := helper.GetCategoryAttr("global", "global", "Kickstart_PrivateHostname") log.Infof("前端主机名: %s", val) */ const ( attrPostfix = "_old" ) // DBHelper DatabaseHelper类 - 继承DB,扩展业务方法 type DBHelper struct { *DB appliancesList []string frontendName string cacheAttrs sync.Map } func NewDBHelper() (*DBHelper, error) { db, err := GetInstance() if err != nil { return nil, err } return &DBHelper{ DB: db, appliancesList: nil, frontendName: "", }, nil } // ==================== 节点查询 ==================== // GetListHostnames 获取所有主机名列表 func (h *DBHelper) GetListHostnames() ([]string, error) { _, err := h.Execute("SELECT name FROM nodes ORDER BY name") if err != nil { return nil, err } rows, err := h.FetchAll() if err != nil { return nil, err } var names []string for _, row := range rows { if name, ok := row["name"]; ok { names = append(names, name.(string)) } } return names, nil } // GetNodesFromNames 从名称列表获取节点 func (h *DBHelper) GetNodesFromNames(names []string, managedOnly bool) ([]map[string]interface{}, error) { // 如果没有提供名称,返回所有节点 if len(names) == 0 { query := "SELECT * FROM nodes" if managedOnly { query = ` SELECT n.* FROM nodes n JOIN node_attrs a ON n.id = a.node_id WHERE a.attr = 'managed' AND a.value = 'true' ` } _, err := h.Execute(query) if err != nil { return nil, err } return h.FetchAll() } // 构建查询条件 conditions := []string{} args := []interface{}{} for _, name := range names { if strings.HasPrefix(name, "select ") { conditions = append(conditions, fmt.Sprintf("name IN (%s)", name[7:])) } else if strings.Contains(name, "%") { conditions = append(conditions, "name LIKE ?") args = append(args, name) } else if strings.HasPrefix(name, "rack") { rackNum := strings.TrimPrefix(name, "rack") conditions = append(conditions, "rack = ?") args = append(args, rackNum) } else if h.IsApplianceName(name) { conditions = append(conditions, `id IN ( SELECT node_id FROM node_attrs WHERE attr = 'appliance' AND value = ? )`) args = append(args, name) } else { hostname, err := h.GetHostname(name) if err == nil { conditions = append(conditions, "name = ?") args = append(args, hostname) } } } if len(conditions) == 0 { return []map[string]interface{}{}, nil } query := "SELECT * FROM nodes WHERE " + strings.Join(conditions, " OR ") _, err := h.Execute(query, args...) if err != nil { return nil, err } nodes, err := h.FetchAll() if err != nil { return nil, err } // 过滤受管节点 if managedOnly { var managed []map[string]interface{} for _, node := range nodes { val := h.GetHostAttr(node["name"].(string), "managed") if val == "true" { managed = append(managed, node) } } return managed, nil } return nodes, nil } // ==================== 设备类型 ==================== // GetAppliancesListText 获取所有设备类型名称 func (h *DBHelper) GetAppliancesListText() []string { if h.appliancesList != nil { return h.appliancesList } _, err := h.Execute("SELECT DISTINCT value FROM node_attrs WHERE attr = 'appliance'") if err != nil { return []string{} } rows, _ := h.FetchAll() var apps []string for _, row := range rows { if val, ok := row["value"]; ok { apps = append(apps, val.(string)) } } h.appliancesList = apps return apps } // IsApplianceName 检查是否为设备类型名称 func (h *DBHelper) IsApplianceName(name string) bool { for _, app := range h.GetAppliancesListText() { if app == name { return true } } return false } // ==================== 主机名解析 ==================== // GetHostname 规范化主机名 - 完全参考Rocks实现 func (h *DBHelper) GetHostname(hostname string) (string, error) { // 如果hostname为空,使用系统主机名 if hostname == "" { hostname, _ = os.Hostname() hostname = strings.Split(hostname, ".")[0] return h.GetHostname(hostname) } // 1. 直接在nodes表中查找 _, err := h.Execute("SELECT * FROM nodes WHERE name = ?", hostname) if err == nil { row, _ := h.FetchOne() if row != nil { return hostname, nil } } // 2. 尝试IP地址反向解析 addr := net.ParseIP(hostname) if addr != nil { names, err := net.LookupAddr(hostname) if err == nil && len(names) > 0 { return h.GetHostname(strings.Split(names[0], ".")[0]) } } // 3. 在networks表中查找IP if addr != nil { _, err := h.Execute(` SELECT n.name FROM nodes n JOIN networks net ON n.id = net.node_id WHERE net.ip = ? `, addr.String()) if err == nil { row, _ := h.FetchOne() if row != nil { return row["name"].(string), nil } } } // 4. 尝试MAC地址 mac := strings.ReplaceAll(hostname, "-", ":") _, err = h.Execute(` SELECT n.name FROM nodes n JOIN networks net ON n.id = net.node_id WHERE net.mac = ? `, mac) if err == nil { row, _ := h.FetchOne() if row != nil { return row["name"].(string), nil } } // 5. 检查别名 _, err = h.Execute(` SELECT n.name FROM nodes n JOIN aliases a ON n.id = a.node_id WHERE a.name = ? `, hostname) if err == nil { row, _ := h.FetchOne() if row != nil { return row["name"].(string), nil } } // 6. 尝试FQDN if strings.Contains(hostname, ".") { parts := strings.Split(hostname, ".") name := parts[0] domain := strings.Join(parts[1:], ".") _, err := h.Execute(` SELECT n.name FROM nodes n JOIN networks net ON n.id = net.node_id JOIN subnets s ON net.subnet_id = s.id WHERE s.dns_zone = ? AND (net.name = ? OR n.name = ?) `, domain, name, name) if err == nil { row, _ := h.FetchOne() if row != nil { return row["name"].(string), nil } } } // 7. 如果以上都失败,抛出异常 return "", fmt.Errorf("无法解析主机名: %s", hostname) } // CheckHostnameValidity 检查主机名有效性 func (h *DBHelper) CheckHostnameValidity(hostname string) error { // 不能包含点 if strings.Contains(hostname, ".") { return fmt.Errorf("主机名 %s 不能包含点号", hostname) } // 不能是rack<数字>格式 if strings.HasPrefix(hostname, "rack") { num := strings.TrimPrefix(hostname, "rack") if _, err := fmt.Sscanf(num, "%d", new(int)); err == nil { return fmt.Errorf("主机名 %s 不能是rack<数字>格式", hostname) } } // 不能是设备类型名称 if h.IsApplianceName(hostname) { return fmt.Errorf("主机名 %s 不能与设备类型名称相同", hostname) } // 检查是否已存在 _, err := h.GetHostname(hostname) if err == nil { return fmt.Errorf("节点 %s 已存在", hostname) } return nil } // ==================== 前端节点 ==================== // GetFrontendName 获取前端节点名称 func (h *DBHelper) GetFrontendName() string { if h.frontendName != "" { return h.frontendName } name := h.GetCategoryAttr("global", "global", "Kickstart_PrivateHostname") if name != "" { h.frontendName = name } return h.frontendName } // ==================== 属性管理 ==================== // GetCategoryIndex 获取类别索引 func (h *DBHelper) GetCategoryIndex(categoryName, categoryIndex string) (map[string]interface{}, map[string]interface{}, error) { // 查询类别和索引 _, err := h.Execute(` SELECT c.id as cid, c.name as cname, i.id as iid, i.name as iname FROM categories c JOIN catindexes i ON c.id = i.category_id WHERE c.name = ? AND i.name = ? `, categoryName, categoryIndex) if err == nil { row, _ := h.FetchOne() if row != nil { category := map[string]interface{}{ "id": row["cid"], "name": row["cname"], } catindex := map[string]interface{}{ "id": row["iid"], "name": row["iname"], "category_id": row["cid"], } return category, catindex, nil } } // 不存在则创建 // 创建类别 _, err = h.Execute("INSERT INTO categories (name) VALUES (?)", categoryName) if err != nil { return nil, nil, err } var catID int64 h.Execute("SELECT last_insert_rowid()") row, _ := h.FetchOne() if row != nil { catID = row["last_insert_rowid()"].(int64) } // 创建索引 _, err = h.Execute( "INSERT INTO catindexes (name, category_id) VALUES (?, ?)", categoryIndex, catID, ) if err != nil { return nil, nil, err } h.Execute("SELECT last_insert_rowid()") row, _ = h.FetchOne() var idxID int64 if row != nil { idxID = row["last_insert_rowid()"].(int64) } category := map[string]interface{}{ "id": catID, "name": categoryName, } catindex := map[string]interface{}{ "id": idxID, "name": categoryIndex, "category_id": catID, } return category, catindex, nil } // SetCategoryAttr 设置类别属性 func (h *DBHelper) SetCategoryAttr(categoryName, catindexName, attr, value string) error { cat, catindex, err := h.GetCategoryIndex(categoryName, catindexName) if err != nil { return err } // 查询现有属性 _, err = h.Execute(` SELECT id, value FROM attributes WHERE attr = ? AND category_id = ? AND catindex_id = ? `, attr, cat["id"], catindex["id"]) if err == nil { row, _ := h.FetchOne() if row != nil { // 更新现有属性 oldValue := row["value"] attrID := row["id"] _, err = h.Execute( "UPDATE attributes SET value = ? WHERE id = ?", value, attrID, ) if err != nil { return err } // 保存旧值 if !strings.HasSuffix(attr, attrPostfix) { h.SetCategoryAttr(categoryName, catindexName, attr+attrPostfix, oldValue.(string)) } return nil } } // 创建新属性 _, err = h.Execute(` INSERT INTO attributes (attr, value, category_id, catindex_id) VALUES (?, ?, ?, ?) `, attr, value, cat["id"], catindex["id"]) return err } // GetCategoryAttr 获取类别属性 func (h *DBHelper) GetCategoryAttr(categoryName, catindexName, attrName string) string { cat, catindex, err := h.GetCategoryIndex(categoryName, catindexName) if err != nil { return "" } _, err = h.Execute(` SELECT value FROM attributes WHERE attr = ? AND category_id = ? AND catindex_id = ? `, attrName, cat["id"], catindex["id"]) if err != nil { return "" } row, _ := h.FetchOne() if row == nil { return "" } return row["value"].(string) } // RemoveCategoryAttr 移除类别属性 func (h *DBHelper) RemoveCategoryAttr(categoryName, catindexName, attrName string) error { cat, catindex, err := h.GetCategoryIndex(categoryName, catindexName) if err != nil { return err } _, err = h.Execute(` DELETE FROM attributes WHERE attr = ? AND category_id = ? AND catindex_id = ? `, attrName, cat["id"], catindex["id"]) if err != nil { return err } // 同时删除对应的_old属性 _, _ = h.Execute(` DELETE FROM attributes WHERE attr = ? AND category_id = ? AND catindex_id = ? `, attrName+attrPostfix, cat["id"], catindex["id"]) return nil } // ==================== 主机属性 ==================== // GetHostAttr 获取主机属性 func (h *DBHelper) GetHostAttr(hostname, attr string) string { // 先从节点直接属性查询 _, err := h.Execute(` SELECT value FROM node_attrs WHERE node_id = (SELECT id FROM nodes WHERE name = ?) AND attr = ? `, hostname, attr) if err == nil { row, _ := h.FetchOne() if row != nil { return row["value"].(string) } } // 使用Rocks的属性解析链查询 query := ` SELECT a.value FROM attributes a JOIN resolvechain r ON a.category_id = r.category_id JOIN hostselections h ON a.category_id = h.category_id AND a.catindex_id = h.selection WHERE h.host_id = (SELECT id FROM nodes WHERE name = ?) AND a.attr = ? ORDER BY r.precedence DESC LIMIT 1 ` _, err = h.Execute(query, hostname, attr) if err != nil { return "" } row, _ := h.FetchOne() if row == nil { return "" } return row["value"].(string) } // GetHostAttrs 获取主机所有属性 func (h *DBHelper) GetHostAttrs(hostname string, showSource bool) map[string]interface{} { attrs := make(map[string]interface{}) // 获取节点基本信息 _, err := h.Execute(` SELECT n.id, n.name, n.rack, n.rank, m.name as membership, a.name as appliance FROM nodes n LEFT JOIN memberships m ON n.membership_id = m.id LEFT JOIN appliances a ON m.appliance_id = a.id WHERE n.name = ? `, hostname) if err == nil { row, _ := h.FetchOne() if row != nil { if showSource { attrs["hostname"] = []interface{}{row["name"], "I"} attrs["rack"] = []interface{}{row["rack"], "I"} attrs["rank"] = []interface{}{row["rank"], "I"} attrs["appliance"] = []interface{}{row["appliance"], "I"} attrs["membership"] = []interface{}{row["membership"], "I"} } else { attrs["hostname"] = row["name"] attrs["rack"] = row["rack"] attrs["rank"] = row["rank"] attrs["appliance"] = row["appliance"] attrs["membership"] = row["membership"] } } } // 获取所有属性 query := ` SELECT a.attr, a.value, CASE WHEN h.host_id IS NOT NULL THEN 'H' ELSE UPPER(SUBSTR(c.name, 1, 1)) END as source FROM attributes a JOIN categories c ON a.category_id = c.id LEFT JOIN hostselections h ON a.category_id = h.category_id AND a.catindex_id = h.selection AND h.host_id = (SELECT id FROM nodes WHERE name = ?) UNION SELECT attr, value, 'N' as source FROM node_attrs WHERE node_id = (SELECT id FROM nodes WHERE name = ?) ` _, err = h.Execute(query, hostname, hostname) if err == nil { rows, _ := h.FetchAll() for _, row := range rows { attr := row["attr"].(string) value := row["value"] if showSource { attrs[attr] = []interface{}{value, row["source"]} } else { attrs[attr] = value } } } return attrs }