imagetools.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package imagetools
  15. import (
  16. "fmt"
  17. "regexp"
  18. "strings"
  19. "yunion.io/x/pkg/util/osprofile"
  20. )
  21. const (
  22. OS_DIST_CENTOS = "CentOS"
  23. OS_DIST_CENTOS_STREAM = "CentOS Stream"
  24. OS_DIST_RHEL = "RHEL"
  25. OS_DIST_FREE_BSD = "FreeBSD"
  26. OS_DIST_UBUNTU_SERVER = "Ubuntu Server"
  27. OS_DIST_UBUNTU = "Ubuntu"
  28. OS_DIST_OPEN_SUSE = "OpenSUSE"
  29. OS_DIST_SUSE = "SUSE"
  30. OS_DIST_DEBIAN = "Debian"
  31. OS_DIST_CORE_OS = "CoreOS"
  32. OS_DIST_EULER_OS = "EulerOS"
  33. OS_DIST_OPEN_EULER = "OpenEuler"
  34. OS_DIST_ALIYUN = "Aliyun"
  35. OS_DIST_DEEPIN = "Deepin"
  36. OS_DIST_ALIBABA_CLOUD_LINUX = "Alibaba Cloud Linux"
  37. OS_DIST_ANOLIS = "Anolis OS"
  38. OS_DIST_ROCKY_LINUX = "Rocky Linux"
  39. OS_DIST_FEDORA = "Fedora"
  40. OS_DIST_ALMA_LINUX = "AlmaLinux"
  41. OS_DIST_AMAZON_LINUX = "Amazon Linux"
  42. OS_DIST_WINDOWS_SERVER = "Windows Server"
  43. OS_DIST_WINDOWS = "Windows"
  44. OS_DIST_KYLIN = "Kylin"
  45. OS_DIST_UOS = "UOS"
  46. OS_DIST_TENCENTOS_SERVER = "TencentOS Server"
  47. OS_DIST_OTHER_LINUX = "Others Linux"
  48. )
  49. func normalizeOsArch(osArch string, imageName string) string {
  50. if len(osArch) > 0 {
  51. switch strings.ToLower(osArch) {
  52. case "x86_64", "amd64", "64", "64bit", "64位":
  53. return osprofile.OS_ARCH_X86_64
  54. case "x86", "x86_32", "32", "32bit", "32位":
  55. return osprofile.OS_ARCH_X86
  56. case "arm", "arm64", "aarch", "aarch64":
  57. return osprofile.OS_ARCH_AARCH64
  58. default:
  59. return osArch
  60. }
  61. } else {
  62. for _, arch := range []string{
  63. "64bit", "64位", "amd64", "x86_64",
  64. "32bit", "32位", "i386", "x86",
  65. "armv8", "arm64", "aarch64", "aarch",
  66. "armv6", "armv7", "armv7s", "arm", "aarch32",
  67. } {
  68. if strings.Contains(strings.ToLower(imageName), arch) {
  69. switch arch {
  70. case "64bit", "64位", "amd64", "x86_64":
  71. return osprofile.OS_ARCH_X86_64
  72. case "32bit", "32位", "i386", "x86":
  73. return osprofile.OS_ARCH_X86_32
  74. case "armv8", "arm64", "aarch64":
  75. return osprofile.OS_ARCH_AARCH64
  76. case "armv6", "armv7", "armv7s", "arm", "aarch32":
  77. return osprofile.OS_ARCH_AARCH32
  78. }
  79. }
  80. }
  81. return osprofile.OS_ARCH_X86_64
  82. }
  83. }
  84. func normalizeOsType(osType string, osDist string) string {
  85. osType = strings.ToLower(osType)
  86. if osType == "linux" {
  87. return osprofile.OS_TYPE_LINUX
  88. } else if osType == "windows" {
  89. return osprofile.OS_TYPE_WINDOWS
  90. } else if strings.HasPrefix(strings.ToLower(osDist), "windows") {
  91. return osprofile.OS_TYPE_WINDOWS
  92. } else {
  93. return osprofile.OS_TYPE_LINUX
  94. }
  95. }
  96. func normalizeOsDistribution(osDist string, imageName string) string {
  97. if len(osDist) == 0 {
  98. osDist = imageName
  99. }
  100. osDist = strings.ToLower(osDist)
  101. if strings.Contains(osDist, "tencentos") {
  102. return OS_DIST_TENCENTOS_SERVER
  103. } else if strings.Contains(osDist, "centos stream") {
  104. return OS_DIST_CENTOS_STREAM
  105. } else if strings.Contains(osDist, "centos") {
  106. return OS_DIST_CENTOS
  107. } else if strings.Contains(osDist, "redhat") || strings.Contains(osDist, "rhel") {
  108. return OS_DIST_RHEL
  109. } else if strings.Contains(osDist, "ubuntu server") {
  110. return OS_DIST_UBUNTU_SERVER
  111. } else if strings.Contains(osDist, "ubuntu") {
  112. return OS_DIST_UBUNTU
  113. } else if strings.Contains(osDist, "opensuse") {
  114. return OS_DIST_OPEN_SUSE
  115. } else if strings.Contains(osDist, "suse") {
  116. return OS_DIST_SUSE
  117. } else if strings.Contains(osDist, "debian") {
  118. return OS_DIST_DEBIAN
  119. } else if strings.Contains(osDist, "coreos") {
  120. return OS_DIST_CORE_OS
  121. } else if strings.Contains(osDist, "aliyun") {
  122. return OS_DIST_ALIYUN
  123. } else if strings.Contains(osDist, "freebsd") {
  124. return OS_DIST_FREE_BSD
  125. } else if strings.Contains(osDist, "euleros") {
  126. return OS_DIST_EULER_OS
  127. } else if strings.Contains(osDist, "openeuler") {
  128. return OS_DIST_OPEN_EULER
  129. } else if strings.Contains(osDist, "alibaba cloud linux") {
  130. return OS_DIST_ALIBABA_CLOUD_LINUX
  131. } else if strings.Contains(osDist, "anolis") {
  132. return OS_DIST_ANOLIS
  133. } else if strings.Contains(osDist, "rocky") {
  134. return OS_DIST_ROCKY_LINUX
  135. } else if strings.Contains(osDist, "fedora") {
  136. return OS_DIST_FEDORA
  137. } else if strings.Contains(osDist, "alma") {
  138. return OS_DIST_ALMA_LINUX
  139. } else if strings.Contains(osDist, "amazon") && strings.Contains(osDist, "linux") {
  140. return OS_DIST_AMAZON_LINUX
  141. } else if strings.Contains(osDist, "kylin") {
  142. return OS_DIST_KYLIN
  143. } else if strings.Contains(osDist, "uos") {
  144. return OS_DIST_UOS
  145. } else if strings.Contains(osDist, "windows") || regexp.MustCompile(".+win(xp|7|8|10|11|2003|2008|2012|2016|2019|2022)*").MatchString(osDist) {
  146. for _, ver := range []string{"2003", "2008", "2012", "2016", "2019", "2022"} {
  147. if strings.Contains(osDist, ver) {
  148. return OS_DIST_WINDOWS_SERVER
  149. }
  150. }
  151. return OS_DIST_WINDOWS
  152. } else if strings.Contains(osDist, "deepin") {
  153. return OS_DIST_DEEPIN
  154. } else {
  155. return OS_DIST_OTHER_LINUX
  156. }
  157. }
  158. var imageVersions = map[string][]string{
  159. // CentOS:补充停更版本和完整迭代,CentOS 8已EOL,Stream补充最新版本
  160. OS_DIST_CENTOS: {"4", "5", "6", "7", "8", "9"},
  161. OS_DIST_CENTOS_STREAM: {"8", "9", "10"},
  162. // RHEL:补充完整主版本,覆盖从5到最新的10
  163. OS_DIST_RHEL: {"5", "6", "7", "8", "9", "10"},
  164. // FreeBSD:补充最新稳定版,覆盖10到15
  165. OS_DIST_FREE_BSD: {"10", "11", "12", "13", "14", "15"},
  166. // Ubuntu:Server版补充LTS版本(每2年一个),Desktop版补充所有主要版本
  167. OS_DIST_UBUNTU_SERVER: {"10.04", "12.04", "14.04", "16.04", "18.04", "20.04", "22.04", "24.04",
  168. "10", "12", "14", "16", "18", "20", "22", "24"},
  169. OS_DIST_UBUNTU: {"10.04", "12.04", "14.04", "15.04", "16.04", "17.04", "18.04", "19.04", "20.04", "21.04", "22.04", "23.04", "24.04",
  170. "10", "12", "14", "16", "17", "18", "19", "20", "21", "22", "23", "24"},
  171. // OpenSUSE:补充Leap版本,SUSE补充SLES主版本
  172. OS_DIST_OPEN_SUSE: {"11", "12", "13", "42", "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6"},
  173. OS_DIST_SUSE: {"10", "11", "12", "15", "15 SP1", "15 SP2", "15 SP3", "15 SP4", "15 SP5"},
  174. // Debian:补充从6到最新的13,覆盖所有稳定版
  175. OS_DIST_DEBIAN: {"6", "7", "8", "9", "10", "11", "12", "13"},
  176. // CoreOS:补充Container Linux和Fedora CoreOS的主要版本
  177. OS_DIST_CORE_OS: {"7", "200", "213", "224", "234", "246", "251", "3033"},
  178. // 欧拉OS:补充openEuler和EulerOS完整版本
  179. OS_DIST_OPEN_EULER: {"2.0 SP1", "2.0 SP2", "2.0 SP3", "2.0 SP8", "3.0", "22.03", "23.09"},
  180. OS_DIST_EULER_OS: {"2"},
  181. // 阿里云Linux:补充1代和2/3代版本
  182. OS_DIST_ALIYUN: {"1", "2.1903", "3.2104", "3.2304"},
  183. // 阿里云轻量版:补充完整版本
  184. OS_DIST_ALIBABA_CLOUD_LINUX: {"2.1903", "3.2104", "3.2304", "3.2404"},
  185. // 龙蜥OS:补充7/8系列完整小版本
  186. OS_DIST_ANOLIS: {"7.6", "7.9", "8.2", "8.4", "8.6", "8.8", "9.0", "9.2"},
  187. // Rocky Linux:补充8/9全系列小版本
  188. OS_DIST_ROCKY_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5"},
  189. // Fedora:补充近年主流版本(33到40)
  190. OS_DIST_FEDORA: {"33", "34", "35", "36", "37", "38", "39", "40"},
  191. // AlmaLinux:补充8/9全系列
  192. OS_DIST_ALMA_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5"},
  193. // Amazon Linux:补充1/2/2023版本
  194. OS_DIST_AMAZON_LINUX: {"2022", "2023", "1", "2"},
  195. // Windows Server:补充完整服务器版本
  196. OS_DIST_WINDOWS_SERVER: {"2003", "2008", "2008 R2", "2012", "2012 R2", "2016", "2019", "2022"},
  197. // Windows 桌面版:补充完整版本
  198. OS_DIST_WINDOWS: {"XP", "Vista", "7", "8", "8.1", "10", "11"},
  199. // 麒麟OS:补充V10各版本和V11
  200. OS_DIST_KYLIN: {"V10", "V10 SP1", "V10 SP2", "V10 SP3", "V11", "Nile"},
  201. // UOS:补充统信UOS完整版本
  202. OS_DIST_UOS: {"V20 1050", "20 1050", "1050", "V20 1060", "1060", "V20 1070", "1070", "V20 1080", "V20 1090", "V23", "Eagle", "V20"},
  203. // 腾讯云OS:补充完整版本
  204. OS_DIST_TENCENTOS_SERVER: {"2.4", "3.1", "3.2", "3.3", "4.0", "4"},
  205. // 其他Linux:预留空列表,可根据实际场景补充
  206. OS_DIST_DEEPIN: {"20", "20.9", "21", "21.9", "22", "22.9", "23", "23.9", "Crimson"},
  207. OS_DIST_OTHER_LINUX: {},
  208. }
  209. func normalizeOsVersion(imageName string, osDist string, osVersion string) string {
  210. if versions, ok := imageVersions[osDist]; ok {
  211. for _, version := range versions {
  212. if len(osVersion) > 0 {
  213. if strings.HasPrefix(osVersion, version) {
  214. return osVersion
  215. }
  216. } else {
  217. parts := strings.Split(strings.ToLower(osDist), " ")
  218. parts = append(parts, fmt.Sprintf(`(?P<verstr>%s[.\d]*)`, version), "")
  219. regexpStr := strings.Join(parts, `[\s-_]*`)
  220. m := regexp.MustCompile(regexpStr).FindAllStringSubmatch(strings.ToLower(imageName), -1)
  221. if m != nil && len(m) > 0 && len(m[0]) > 1 {
  222. verStr := m[0][1]
  223. if strings.HasPrefix(verStr, version) && len(verStr) > len(version) && !strings.HasPrefix(verStr, version+".") {
  224. verStr = version + "." + verStr[len(version):]
  225. }
  226. return verStr
  227. }
  228. }
  229. }
  230. for i := len(versions) - 1; i > 0; i-- {
  231. if strings.Contains(imageName, versions[i]) {
  232. return versions[i]
  233. }
  234. }
  235. }
  236. return ""
  237. }
  238. func normalizeOsLang(imageName string) string {
  239. for _, sep := range []string{" ", "-", "_"} {
  240. lang := normalizeOsLang2(imageName, sep)
  241. if len(lang) > 0 {
  242. return lang
  243. }
  244. }
  245. return ""
  246. }
  247. func normalizeOsLang2(imageName string, sep string) string {
  248. parts := strings.Split(imageName, sep)
  249. for _, o := range parts {
  250. switch strings.ToLower(o) {
  251. case "en":
  252. return "en_US"
  253. case "cn":
  254. return "zh_CN"
  255. case "zh":
  256. return "zh_CN"
  257. case "中文版":
  258. return "zh_CN"
  259. case "英文版":
  260. return "en_US"
  261. }
  262. }
  263. return ""
  264. }
  265. func normalizeOsBios(imageName string, osArch string) string {
  266. if osArch != "" {
  267. switch osArch {
  268. case osprofile.OS_ARCH_ARM, osprofile.OS_ARCH_AARCH32, osprofile.OS_ARCH_AARCH64:
  269. return osprofile.OS_BOOT_UEFI
  270. }
  271. }
  272. for _, sep := range []string{" ", "-", "_"} {
  273. lang := normalizeOsBios2(imageName, sep)
  274. if len(lang) > 0 {
  275. return lang
  276. }
  277. }
  278. return ""
  279. }
  280. func normalizeOsBios2(imageName string, sep string) string {
  281. parts := strings.Split(imageName, sep)
  282. for _, o := range parts {
  283. switch strings.ToLower(o) {
  284. case "uefi":
  285. return osprofile.OS_BOOT_UEFI
  286. }
  287. }
  288. return osprofile.OS_BOOT_BIOS
  289. }
  290. type ImageInfo struct {
  291. Name string
  292. OsArch string
  293. OsType string
  294. OsDistro string
  295. OsVersion string
  296. OsFullVersion string
  297. OsLang string
  298. OsBios string
  299. }
  300. func (i ImageInfo) GetFullOsName() string {
  301. parts := make([]string, 0)
  302. if len(i.OsDistro) > 0 {
  303. parts = append(parts, i.OsDistro)
  304. } else if len(i.OsType) > 0 {
  305. parts = append(parts, string(i.OsType))
  306. }
  307. if len(i.OsVersion) > 0 {
  308. parts = append(parts, i.OsVersion)
  309. }
  310. if len(i.OsArch) > 0 {
  311. parts = append(parts, i.OsArch)
  312. }
  313. if len(i.OsBios) > 0 {
  314. parts = append(parts, i.OsBios)
  315. }
  316. if len(i.OsLang) > 0 {
  317. parts = append(parts, i.OsLang)
  318. }
  319. return strings.Join(parts, " ")
  320. }
  321. func NormalizeImageInfo(imageName, osArch, osType, osDist, osVersion string) ImageInfo {
  322. info := ImageInfo{}
  323. info.Name = imageName
  324. info.OsDistro = normalizeOsDistribution(osDist, imageName)
  325. info.OsType = normalizeOsType(osType, info.OsDistro)
  326. info.OsArch = normalizeOsArch(osArch, imageName)
  327. info.OsVersion = normalizeOsVersion(imageName, info.OsDistro, osVersion)
  328. info.OsFullVersion = osVersion
  329. info.OsLang = normalizeOsLang(imageName)
  330. info.OsBios = normalizeOsBios(imageName, info.OsArch)
  331. return info
  332. }