manager.go 90 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229
  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 baremetal
  15. import (
  16. "context"
  17. "fmt"
  18. "io/fs"
  19. "io/ioutil"
  20. "net"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "path/filepath"
  25. "reflect"
  26. "strconv"
  27. "strings"
  28. "sync"
  29. "time"
  30. "yunion.io/x/cloudmux/pkg/apis/compute"
  31. "yunion.io/x/jsonutils"
  32. "yunion.io/x/log"
  33. "yunion.io/x/pkg/errors"
  34. "yunion.io/x/pkg/util/httputils"
  35. "yunion.io/x/pkg/util/netutils"
  36. "yunion.io/x/pkg/util/regutils"
  37. "yunion.io/x/pkg/util/seclib"
  38. "yunion.io/x/pkg/util/sets"
  39. "yunion.io/x/pkg/util/workqueue"
  40. "yunion.io/x/pkg/utils"
  41. "yunion.io/x/onecloud/pkg/apis"
  42. api "yunion.io/x/onecloud/pkg/apis/compute"
  43. baremetalapi "yunion.io/x/onecloud/pkg/apis/compute/baremetal"
  44. apiidenty "yunion.io/x/onecloud/pkg/apis/identity"
  45. o "yunion.io/x/onecloud/pkg/baremetal/options"
  46. "yunion.io/x/onecloud/pkg/baremetal/profiles"
  47. "yunion.io/x/onecloud/pkg/baremetal/pxe"
  48. baremetalstatus "yunion.io/x/onecloud/pkg/baremetal/status"
  49. "yunion.io/x/onecloud/pkg/baremetal/tasks"
  50. baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
  51. "yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
  52. "yunion.io/x/onecloud/pkg/baremetal/utils/disktool"
  53. "yunion.io/x/onecloud/pkg/baremetal/utils/grub"
  54. "yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
  55. raid2 "yunion.io/x/onecloud/pkg/baremetal/utils/raid"
  56. raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
  57. "yunion.io/x/onecloud/pkg/baremetal/utils/raid/mdadm"
  58. "yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
  59. "yunion.io/x/onecloud/pkg/cloudcommon/types"
  60. "yunion.io/x/onecloud/pkg/compute/baremetal"
  61. "yunion.io/x/onecloud/pkg/hostman/guestfs"
  62. "yunion.io/x/onecloud/pkg/hostman/guestfs/sshpart"
  63. deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
  64. "yunion.io/x/onecloud/pkg/httperrors"
  65. "yunion.io/x/onecloud/pkg/mcclient"
  66. "yunion.io/x/onecloud/pkg/mcclient/auth"
  67. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  68. "yunion.io/x/onecloud/pkg/util/dhcp"
  69. "yunion.io/x/onecloud/pkg/util/fileutils2"
  70. "yunion.io/x/onecloud/pkg/util/influxdb"
  71. "yunion.io/x/onecloud/pkg/util/procutils"
  72. "yunion.io/x/onecloud/pkg/util/redfish"
  73. "yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
  74. "yunion.io/x/onecloud/pkg/util/ssh"
  75. "yunion.io/x/onecloud/pkg/util/sysutils"
  76. )
  77. type SBaremetalManager struct {
  78. Agent *SBaremetalAgent
  79. configPath string
  80. baremetals *sBaremetalMap
  81. }
  82. func NewBaremetalManager(agent *SBaremetalAgent) (*SBaremetalManager, error) {
  83. bmPaths := o.Options.BaremetalsPath
  84. err := os.MkdirAll(bmPaths, 0755)
  85. if err != nil {
  86. return nil, err
  87. }
  88. return &SBaremetalManager{
  89. Agent: agent,
  90. configPath: bmPaths,
  91. baremetals: newBaremetalMap(),
  92. }, nil
  93. }
  94. func (m *SBaremetalManager) killAllIPMITool() {
  95. procutils.NewCommand("killall", "-9", "ipmitool").Run()
  96. }
  97. func (m *SBaremetalManager) GetClientSession() *mcclient.ClientSession {
  98. return m.Agent.GetAdminSession()
  99. }
  100. func (m *SBaremetalManager) GetPublicClientSession() *mcclient.ClientSession {
  101. return m.Agent.GetPublicAdminSession()
  102. }
  103. func (m *SBaremetalManager) GetZoneId() string {
  104. return m.Agent.Zone.Id
  105. }
  106. func (m *SBaremetalManager) GetZoneName() string {
  107. return m.Agent.Zone.Name
  108. }
  109. func (m *SBaremetalManager) loadConfigs() ([]fs.DirEntry, error) {
  110. m.killAllIPMITool()
  111. files, err := os.ReadDir(m.configPath)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return files, nil
  116. }
  117. func (m *SBaremetalManager) initBaremetals(ctx context.Context, files []fs.DirEntry) error {
  118. bmIds := make([]string, 0)
  119. for _, file := range files {
  120. if file.IsDir() && regutils.MatchUUID(file.Name()) {
  121. bmIds = append(bmIds, file.Name())
  122. }
  123. }
  124. errsChannel := make(chan error, len(bmIds))
  125. initBaremetal := func(i int) {
  126. bmId := bmIds[i]
  127. err := m.InitBaremetal(ctx, bmId, true)
  128. if err != nil {
  129. errsChannel <- err
  130. return
  131. }
  132. }
  133. workqueue.Parallelize(4, len(bmIds), initBaremetal)
  134. errs := make([]error, 0)
  135. if len(errsChannel) > 0 {
  136. length := len(errsChannel)
  137. for ; length > 0; length-- {
  138. errs = append(errs, <-errsChannel)
  139. }
  140. }
  141. return errors.NewAggregate(errs)
  142. }
  143. func (m *SBaremetalManager) InitBaremetal(ctx context.Context, bmId string, update bool) error {
  144. session := m.GetClientSession()
  145. var err error
  146. var desc jsonutils.JSONObject
  147. if update {
  148. desc, err = m.updateBaremetal(session, bmId)
  149. } else {
  150. desc, err = m.fetchBaremetal(session, bmId)
  151. }
  152. if err != nil {
  153. return err
  154. }
  155. isBaremetal, _ := desc.Bool("is_baremetal")
  156. if !isBaremetal {
  157. return errors.Error("not a baremetal???")
  158. }
  159. bmInstance, err := m.AddBaremetal(ctx, desc)
  160. if err != nil {
  161. return err
  162. }
  163. if update {
  164. bmObj := bmInstance.(*SBaremetalInstance)
  165. if !sets.NewString(INIT, PREPARE, UNKNOWN).Has(bmObj.GetStatus()) {
  166. if !bmObj.IsHypervisorHost() {
  167. bmObj.SyncStatusBackground()
  168. }
  169. }
  170. }
  171. return nil
  172. }
  173. func (m *SBaremetalManager) CleanBaremetal(bmId string) {
  174. bm := m.baremetals.Pop(bmId)
  175. if bm != nil {
  176. bm.Stop()
  177. }
  178. bm.clearBootIsoImage()
  179. path := bm.GetDir()
  180. procutils.NewCommand("rm", "-fr", path).Run()
  181. }
  182. func (m *SBaremetalManager) updateBaremetal(session *mcclient.ClientSession, bmId string) (jsonutils.JSONObject, error) {
  183. params := jsonutils.NewDict()
  184. params.Add(jsonutils.JSONTrue, "is_baremetal")
  185. params.Add(jsonutils.JSONTrue, "not_sync_config")
  186. obj, err := modules.Hosts.Put(session, bmId, params)
  187. if err != nil {
  188. return nil, err
  189. }
  190. log.Infof("Baremetal %s update success", bmId)
  191. return obj, nil
  192. }
  193. func (m *SBaremetalManager) fetchBaremetal(session *mcclient.ClientSession, bmId string) (jsonutils.JSONObject, error) {
  194. obj, err := modules.Hosts.Get(session, bmId, nil)
  195. if err != nil {
  196. return nil, err
  197. }
  198. log.Infof("Baremetal %s update success", bmId)
  199. return obj, nil
  200. }
  201. func (m *SBaremetalManager) AddBaremetal(ctx context.Context, desc jsonutils.JSONObject) (pxe.IBaremetalInstance, error) {
  202. id, err := desc.GetString("id")
  203. if err != nil {
  204. return nil, fmt.Errorf("Not found baremetal id in desc %s", desc)
  205. }
  206. if instance, ok := m.baremetals.Get(id); ok {
  207. return instance, instance.SaveDesc(ctx, desc)
  208. }
  209. bm, err := newBaremetalInstance(ctx, m, desc)
  210. if err != nil {
  211. return nil, err
  212. }
  213. m.baremetals.Add(bm)
  214. return bm, nil
  215. }
  216. func (m *SBaremetalManager) GetBaremetals() []*SBaremetalInstance {
  217. objs := make([]*SBaremetalInstance, 0)
  218. getter := func(key, val interface{}) bool {
  219. objs = append(objs, val.(*SBaremetalInstance))
  220. return true
  221. }
  222. m.baremetals.Range(getter)
  223. return objs
  224. }
  225. func (m *SBaremetalManager) GetBaremetalById(bmId string) *SBaremetalInstance {
  226. obj, _ := m.baremetals.Get(bmId)
  227. return obj
  228. }
  229. func (m *SBaremetalManager) GetBaremetalByMac(mac net.HardwareAddr) pxe.IBaremetalInstance {
  230. var obj pxe.IBaremetalInstance
  231. getter := func(key, val interface{}) bool {
  232. instance := val.(*SBaremetalInstance)
  233. if instance.GetNicByMac(mac) != nil {
  234. obj = instance
  235. // stop the iteration
  236. return false
  237. }
  238. // not found, continue iteration
  239. return true
  240. }
  241. m.baremetals.Range(getter)
  242. return obj
  243. }
  244. type BmRegisterInput struct {
  245. // context with timeout
  246. Ctx context.Context
  247. R *http.Request
  248. W http.ResponseWriter
  249. // For notify web server close this connection
  250. C chan struct{}
  251. // remote server ssh info
  252. SshPort int
  253. SshPasswd string
  254. Hostname string
  255. RemoteIp string
  256. // ipmi info
  257. Username string
  258. Password string
  259. IpAddr string
  260. }
  261. func (i *BmRegisterInput) responseSucc(bmId string) {
  262. fmt.Fprintf(i.W, "%s", bmId)
  263. close(i.C)
  264. }
  265. func (i *BmRegisterInput) responseErr(ctx context.Context, err error) {
  266. httperrors.GeneralServerError(ctx, i.W, err)
  267. close(i.C)
  268. }
  269. func (i *BmRegisterInput) isTimeout() bool {
  270. select {
  271. case <-i.Ctx.Done():
  272. return true
  273. default:
  274. return false
  275. }
  276. }
  277. // delay task
  278. func (m *SBaremetalManager) RegisterBaremetal(ctx context.Context, userCred mcclient.TokenCredential, input *BmRegisterInput) {
  279. adminWire, err := m.checkNetworkFromIp(input.RemoteIp)
  280. if input.isTimeout() {
  281. return
  282. } else if err != nil {
  283. input.responseErr(ctx, httperrors.NewBadRequestError("Verify network failed: %s", err))
  284. return
  285. }
  286. sshCli, err := m.checkSshInfo(input)
  287. if input.isTimeout() {
  288. return
  289. } else if err != nil {
  290. input.responseErr(ctx, httperrors.NewBadRequestError("SSH verify failed: %s", err))
  291. return
  292. }
  293. isIpv6Addr := false
  294. if strings.Contains(input.RemoteIp, ":") {
  295. isIpv6Addr = true
  296. }
  297. input.IpAddr, err = m.fetchIpmiIp(sshCli, isIpv6Addr)
  298. log.Infof("find ipmi addr %s", input.IpAddr)
  299. if input.isTimeout() {
  300. return
  301. } else if err != nil {
  302. input.responseErr(ctx, httperrors.NewBadRequestError("Fetch ipmi address failed: %s", err))
  303. return
  304. }
  305. log.Infof("Find ipmi address %s", input.IpAddr)
  306. ipmiWire, err := m.checkNetworkFromIp(input.IpAddr)
  307. if input.isTimeout() {
  308. return
  309. } else if err != nil {
  310. input.responseErr(ctx, httperrors.NewBadRequestError("Verify network failed: %s", err))
  311. return
  312. }
  313. ipmiLanChannel, ipmiMac, err := m.checkIpmiInfo(ctx, input.Username, input.Password, input.IpAddr)
  314. if input.isTimeout() {
  315. return
  316. } else if err != nil {
  317. input.responseErr(ctx, httperrors.NewBadRequestError("IPMI login info not correct: %s", err))
  318. return
  319. }
  320. err, registered := m.verifyMacAddr(sshCli)
  321. if input.isTimeout() {
  322. return
  323. } else if err != nil {
  324. input.responseErr(ctx, httperrors.NewBadRequestError("Verify mac address failed: %s", err))
  325. return
  326. }
  327. registerTask := tasks.NewBaremetalRegisterTask(
  328. userCred, m, sshCli, input.Hostname, input.RemoteIp,
  329. input.Username, input.Password, input.IpAddr,
  330. ipmiMac, ipmiLanChannel, adminWire, ipmiWire,
  331. )
  332. var bmId string
  333. if !registered {
  334. bmId, err = registerTask.CreateBaremetal(ctx)
  335. } else {
  336. bmId, err = registerTask.UpdateBaremetal(ctx)
  337. }
  338. if err != nil {
  339. input.responseErr(ctx, httperrors.NewInternalServerError("%v", err))
  340. return
  341. }
  342. input.responseSucc(bmId)
  343. registerTask.DoPrepare(ctx, sshCli, registered)
  344. }
  345. func (m *SBaremetalManager) fetchIpmiIp(sshCli *ssh.Client, isIpv6Addr bool) (string, error) {
  346. if !isIpv6Addr {
  347. res, err := sshCli.RawRun(`/usr/bin/ipmitool lan print | grep "IP Address "`)
  348. if err != nil {
  349. return "", err
  350. }
  351. if len(res) == 1 {
  352. segs := strings.Fields(res[0])
  353. if len(segs) == 4 {
  354. return strings.TrimSpace(segs[3]), nil
  355. }
  356. }
  357. } else {
  358. res, err := sshCli.Run(`/usr/bin/ipmitool lan6 print`)
  359. if err != nil {
  360. return "", err
  361. }
  362. for i, line := range res {
  363. if strings.HasPrefix(line, "IPv6 Static Address") || strings.HasPrefix(line, "IPv6 Dynamic Address") {
  364. if len(res)-i > 3 {
  365. enabledSegs := strings.Fields(res[i+1])
  366. log.Infof("enabled segs %#v", enabledSegs)
  367. if len(enabledSegs) != 2 || enabledSegs[0] != "Enabled:" || enabledSegs[1] != "yes" {
  368. continue
  369. }
  370. statusSegs := strings.Fields(res[i+3])
  371. log.Infof("status segs %#v", statusSegs)
  372. if len(enabledSegs) != 2 || statusSegs[0] != "Status:" || statusSegs[1] != "active" {
  373. continue
  374. }
  375. addrSegs := strings.Fields(res[i+2])
  376. if len(addrSegs) != 2 || addrSegs[0] != "Address:" {
  377. continue
  378. }
  379. ipv6Addr := strings.Split(addrSegs[1], "/")
  380. return ipv6Addr[0], nil
  381. }
  382. }
  383. }
  384. }
  385. return "", fmt.Errorf("Failed to find ipmi ip address")
  386. }
  387. func (m *SBaremetalManager) checkNetworkFromIp(ip string) (string, error) {
  388. params := jsonutils.NewDict()
  389. params.Set("ip", jsonutils.NewString(ip))
  390. params.Set("scope", jsonutils.NewString("system"))
  391. params.Set("is_classic", jsonutils.JSONTrue)
  392. params.Set("provider", jsonutils.NewString(api.CLOUD_PROVIDER_ONECLOUD))
  393. params.Set("limit", jsonutils.NewInt(0))
  394. // use default vpc
  395. params.Set("vpc", jsonutils.NewString(api.DEFAULT_VPC_ID))
  396. res, err := modules.Networks.List(m.GetClientSession(), params)
  397. if err != nil {
  398. return "", fmt.Errorf("Fetch network by ip %s failed: %s", ip, err)
  399. }
  400. if len(res.Data) != 1 {
  401. return "", fmt.Errorf("Can't find network from ip %s", ip)
  402. }
  403. return res.Data[0].GetString("wire_id")
  404. }
  405. func (m *SBaremetalManager) verifyMacAddr(sshCli *ssh.Client) (error, bool) {
  406. output, err := sshCli.Run("/lib/mos/lsnic")
  407. if err != nil {
  408. return err, false
  409. }
  410. nicinfo := sysutils.ParseNicInfo(output)
  411. if len(nicinfo) == 0 {
  412. return fmt.Errorf("Can't get nic info"), false
  413. }
  414. var registered bool
  415. params := jsonutils.NewDict()
  416. for _, nic := range nicinfo {
  417. if len(nic.Mac) > 0 {
  418. params.Set("any_mac", jsonutils.NewString(nic.Mac.String()))
  419. params.Set("scope", jsonutils.NewString("system"))
  420. res, err := modules.Hosts.List(m.GetClientSession(), params)
  421. if err != nil {
  422. return fmt.Errorf("Get hosts info failed: %s", err), false
  423. }
  424. if len(res.Data) > 0 {
  425. registered = true
  426. }
  427. }
  428. }
  429. return nil, registered
  430. }
  431. func (m *SBaremetalManager) checkSshInfo(input *BmRegisterInput) (*ssh.Client, error) {
  432. sshCLi, err := ssh.NewClient(input.RemoteIp, input.SshPort, "root", input.SshPasswd, "")
  433. if err != nil {
  434. return nil, fmt.Errorf("Ssh connect failed: %s", err)
  435. }
  436. var RETRY, MAX_TRIES = 0, 3
  437. for RETRY = 0; RETRY < MAX_TRIES; RETRY++ {
  438. _, err := sshCLi.Run("/bin/ls")
  439. if err != nil {
  440. log.Errorf("Exec remote command failed: %s", err)
  441. time.Sleep(time.Second * 1)
  442. } else {
  443. break
  444. }
  445. }
  446. if RETRY >= MAX_TRIES {
  447. return nil, fmt.Errorf("SSH login info not correct??")
  448. }
  449. return sshCLi, nil
  450. }
  451. func (m *SBaremetalManager) checkIpmiInfo(ctx context.Context, username, password, ipAddr string) (uint8, net.HardwareAddr, error) {
  452. lanPlusTool := ipmitool.NewLanPlusIPMI(ipAddr, username, password)
  453. sysInfo, err := ipmitool.GetSysInfo(lanPlusTool)
  454. if err != nil {
  455. return 0, nil, errors.Wrap(err, "GetSysInfo")
  456. }
  457. profile, err := profiles.GetProfile(ctx, sysInfo)
  458. if err != nil {
  459. return 0, nil, errors.Wrap(err, "GetProfile")
  460. }
  461. for _, lanChannel := range profile.LanChannels {
  462. config, err := ipmitool.GetLanConfig(lanPlusTool, lanChannel)
  463. if err != nil {
  464. log.Errorf("GetLanConfig failed %s", err)
  465. continue
  466. }
  467. if len(config.Mac) == 0 {
  468. continue
  469. }
  470. return lanChannel, config.Mac, nil
  471. }
  472. return 0, nil, fmt.Errorf("Ipmi can't fetch lan config")
  473. }
  474. func (m *SBaremetalManager) Stop() {
  475. for _, bm := range m.GetBaremetals() {
  476. bm.Stop()
  477. }
  478. }
  479. type sBaremetalMap struct {
  480. *sync.Map
  481. }
  482. func newBaremetalMap() *sBaremetalMap {
  483. return &sBaremetalMap{
  484. Map: new(sync.Map),
  485. }
  486. }
  487. func (m *sBaremetalMap) Add(bm *SBaremetalInstance) {
  488. m.Store(bm.GetId(), bm)
  489. }
  490. func (m *sBaremetalMap) Get(id string) (*SBaremetalInstance, bool) {
  491. obj, ok := m.Load(id)
  492. if !ok {
  493. return nil, false
  494. }
  495. return obj.(*SBaremetalInstance), true
  496. }
  497. func (m *sBaremetalMap) Delete(id string) {
  498. m.Map.Delete(id)
  499. }
  500. func (m *sBaremetalMap) Pop(id string) *SBaremetalInstance {
  501. obj, exist := m.Get(id)
  502. if exist {
  503. m.Delete(id)
  504. }
  505. return obj
  506. }
  507. type SBaremetalInstance struct {
  508. manager *SBaremetalManager
  509. desc *jsonutils.JSONDict
  510. descLock *sync.Mutex
  511. taskQueue *tasks.TaskQueue
  512. server baremetaltypes.IBaremetalServer
  513. serverLock *sync.Mutex
  514. profile *baremetalapi.BaremetalProfileSpec
  515. cronJobs []IBaremetalCronJob
  516. }
  517. func newBaremetalInstance(ctx context.Context, man *SBaremetalManager, desc jsonutils.JSONObject) (*SBaremetalInstance, error) {
  518. bm := &SBaremetalInstance{
  519. manager: man,
  520. desc: desc.(*jsonutils.JSONDict),
  521. descLock: new(sync.Mutex),
  522. taskQueue: tasks.NewTaskQueue(),
  523. serverLock: new(sync.Mutex),
  524. }
  525. bm.cronJobs = []IBaremetalCronJob{
  526. NewLogFetchJob(bm, time.Duration(o.Options.LogFetchIntervalSeconds)*time.Second),
  527. NewSendMetricsJob(bm, time.Duration(o.Options.SendMetricsIntervalSeconds)*time.Second),
  528. NewStatusProbeJob(bm, time.Duration(o.Options.StatusProbeIntervalSeconds)*time.Second),
  529. }
  530. err := os.MkdirAll(bm.GetDir(), 0755)
  531. if err != nil {
  532. return nil, err
  533. }
  534. err = bm.SaveDesc(ctx, desc)
  535. if err != nil {
  536. return nil, err
  537. }
  538. bm.loadServer()
  539. return bm, nil
  540. }
  541. func (b *SBaremetalInstance) IsHypervisorHost() bool {
  542. hostType, _ := b.desc.GetString("host_type")
  543. return utils.IsInStringArray(hostType, []string{api.HOST_TYPE_KVM, api.HOST_TYPE_HYPERVISOR})
  544. }
  545. func (b *SBaremetalInstance) GetDHCPServerIP() (net.IP, error) {
  546. return b.manager.Agent.GetDHCPServerIP()
  547. }
  548. func (b *SBaremetalInstance) GetClientSession() *mcclient.ClientSession {
  549. return b.manager.GetClientSession()
  550. }
  551. func (b *SBaremetalInstance) GetPublicClientSession() *mcclient.ClientSession {
  552. return b.manager.GetPublicClientSession()
  553. }
  554. func (b *SBaremetalInstance) Keyword() string {
  555. return "host"
  556. }
  557. func (b *SBaremetalInstance) GetId() string {
  558. id, err := b.desc.GetString("id")
  559. if err != nil {
  560. log.Errorf("Get id from desc %s error: %v", b.desc.String(), err)
  561. }
  562. return id
  563. }
  564. func (b *SBaremetalInstance) GetName() string {
  565. id, err := b.desc.GetString("name")
  566. if err != nil {
  567. log.Fatalf("Get name from desc %s error: %v", b.desc.String(), err)
  568. }
  569. return id
  570. }
  571. func (b *SBaremetalInstance) Stop() {
  572. // TODO:
  573. }
  574. func (b *SBaremetalInstance) GetDir() string {
  575. return filepath.Join(b.manager.configPath, b.GetId())
  576. }
  577. func (b *SBaremetalInstance) GetDescFilePath() string {
  578. return filepath.Join(b.GetDir(), "desc")
  579. }
  580. func (b *SBaremetalInstance) GetServerDescFilePath() string {
  581. return filepath.Join(b.GetDir(), "server")
  582. }
  583. func (b *SBaremetalInstance) GetSSHConfigFilePath() string {
  584. return filepath.Join(b.GetDir(), "ssh")
  585. }
  586. func (b *SBaremetalInstance) GetStatus() string {
  587. status, err := b.desc.GetString("status")
  588. if err != nil {
  589. log.Fatalf("Get status from desc error: %v", err)
  590. }
  591. return status
  592. }
  593. func (b *SBaremetalInstance) AutoSaveDesc(ctx context.Context) error {
  594. return b.SaveDesc(ctx, nil)
  595. }
  596. func (b *SBaremetalInstance) SaveDesc(ctx context.Context, desc jsonutils.JSONObject) error {
  597. b.descLock.Lock()
  598. defer b.descLock.Unlock()
  599. if desc != nil {
  600. if b.desc != nil && b.desc.Contains("server_id") && !desc.Contains("server_id") {
  601. if b.server != nil {
  602. desc.(*jsonutils.JSONDict).Set("server_id", jsonutils.NewString(b.server.GetId()))
  603. }
  604. }
  605. b.desc = desc.(*jsonutils.JSONDict)
  606. if b.desc.Contains("sys_info") {
  607. sysInfo := types.SSystemInfo{}
  608. err := b.desc.Unmarshal(&sysInfo, "sys_info")
  609. if err != nil {
  610. return errors.Wrap(err, "Unmarshal sys_info")
  611. }
  612. b.profile, err = profiles.GetProfile(ctx, &sysInfo)
  613. if err != nil {
  614. return errors.Wrap(err, "GetProfile")
  615. }
  616. }
  617. }
  618. return os.WriteFile(b.GetDescFilePath(), []byte(b.desc.String()), 0644)
  619. }
  620. func (b *SBaremetalInstance) loadServer() {
  621. b.serverLock.Lock()
  622. defer b.serverLock.Unlock()
  623. if !b.desc.Contains("server_id") {
  624. return
  625. }
  626. descPath := b.GetServerDescFilePath()
  627. desc, err := os.ReadFile(descPath)
  628. if err != nil {
  629. log.Errorf("Failed to read server desc %s: %v", descPath, err)
  630. return
  631. }
  632. descObj, err := jsonutils.Parse(desc)
  633. if err != nil {
  634. log.Errorf("Failed to parse server json string: %v", err)
  635. return
  636. }
  637. srv, err := newBaremetalServer(b, descObj.(*jsonutils.JSONDict))
  638. if err != nil {
  639. log.Errorf("New server error: %v", err)
  640. return
  641. }
  642. if bmSrvId, _ := b.desc.GetString("server_id"); srv.GetId() != bmSrvId {
  643. log.Errorf("Server id %q not equal baremetal %q server id %q", srv.GetId(), b.GetName(), bmSrvId)
  644. return
  645. }
  646. b.server = srv
  647. }
  648. func (b *SBaremetalInstance) SaveSSHConfig(remoteAddr string, key string) error {
  649. var err error
  650. key, err = utils.EncryptAESBase64(b.GetId(), key)
  651. if err != nil {
  652. return err
  653. }
  654. sshConf := types.SSHConfig{
  655. Username: "root",
  656. Password: key,
  657. RemoteIP: remoteAddr,
  658. }
  659. conf := jsonutils.Marshal(sshConf)
  660. err = os.WriteFile(b.GetSSHConfigFilePath(), []byte(conf.String()), 0644)
  661. if err != nil {
  662. return err
  663. }
  664. b.SyncSSHConfig(sshConf)
  665. b.clearBootIso()
  666. return err
  667. }
  668. func (b *SBaremetalInstance) GetSSHConfig() (*types.SSHConfig, error) {
  669. path := b.GetSSHConfigFilePath()
  670. content, err := os.ReadFile(path)
  671. if err != nil {
  672. if os.IsNotExist(err) {
  673. return nil, nil
  674. }
  675. return nil, err
  676. }
  677. conf := types.SSHConfig{}
  678. obj, err := jsonutils.Parse(content)
  679. if err != nil {
  680. return nil, err
  681. }
  682. err = obj.Unmarshal(&conf)
  683. if err != nil {
  684. return nil, err
  685. }
  686. if len(conf.RemoteIP) == 0 {
  687. return nil, nil
  688. }
  689. conf.Password, err = utils.DescryptAESBase64(b.GetId(), conf.Password)
  690. if err != nil {
  691. return nil, err
  692. }
  693. return &conf, nil
  694. }
  695. func (b *SBaremetalInstance) TestSSHConfig() bool {
  696. conf, err := b.GetSSHConfig()
  697. if err != nil {
  698. return false
  699. }
  700. if conf == nil {
  701. return false
  702. }
  703. sshCli, err := ssh.NewClient(conf.RemoteIP, 22, "root", conf.Password, "")
  704. if err != nil {
  705. return false
  706. }
  707. ret, err := sshCli.Run("whoami")
  708. if err != nil {
  709. return false
  710. }
  711. if strings.Contains(strings.Join(ret, ""), "root") {
  712. return true
  713. }
  714. return false
  715. }
  716. func (b *SBaremetalInstance) ClearSSHConfig() {
  717. path := b.GetSSHConfigFilePath()
  718. err := os.Remove(path)
  719. if err != nil {
  720. log.V(2).Warningf("Clear ssh config %s error: %v", path, err)
  721. }
  722. emptyConfig := types.SSHConfig{
  723. Username: "None",
  724. Password: "None",
  725. RemoteIP: "None",
  726. }
  727. err = b.SyncSSHConfig(emptyConfig)
  728. if err != nil {
  729. log.Errorf("Sync emtpy SSH config error: %v", err)
  730. }
  731. }
  732. func (b *SBaremetalInstance) SyncSSHConfig(conf types.SSHConfig) error {
  733. session := b.manager.GetClientSession()
  734. var info *api.HostLoginInfo
  735. if len(conf.RemoteIP) > 0 {
  736. var err error
  737. // encrypt twice
  738. conf.Password, err = utils.EncryptAESBase64(b.GetId(), conf.Password)
  739. if err != nil {
  740. return err
  741. }
  742. info = &api.HostLoginInfo{
  743. Username: conf.Username,
  744. Password: conf.Password,
  745. Ip: conf.RemoteIP,
  746. }
  747. } else {
  748. info = &api.HostLoginInfo{
  749. Username: "None",
  750. Password: "None",
  751. Ip: "None",
  752. }
  753. }
  754. data := info.JSON(info)
  755. _, err := modules.Hosts.SetMetadata(session, b.GetId(), data)
  756. return err
  757. }
  758. func (b *SBaremetalInstance) SyncStatusBackground() {
  759. go func() {
  760. b.AutoSyncAllStatus(context.Background())
  761. }()
  762. }
  763. func (b *SBaremetalInstance) InitializeServer(s *mcclient.ClientSession, name string) error {
  764. params := jsonutils.NewDict()
  765. params.Set("name", jsonutils.NewString(name))
  766. _, err := modules.Hosts.PerformAction(s, b.GetId(), "initialize", params)
  767. return err
  768. }
  769. func (b *SBaremetalInstance) ServerLoadDesc(ctx context.Context) error {
  770. res, err := modules.Hosts.Get(b.manager.GetClientSession(), b.GetId(), nil)
  771. if err != nil {
  772. return err
  773. }
  774. b.SaveDesc(ctx, res)
  775. sid, err := res.GetString("server_id")
  776. if err == nil {
  777. sDesc := jsonutils.NewDict()
  778. sDesc.Set("uuid", jsonutils.NewString(sid))
  779. b.server, err = newBaremetalServer(b, sDesc)
  780. return err
  781. } else {
  782. return nil
  783. }
  784. }
  785. func PowerStatusToBaremetalStatus(status types.PowerStatus) string {
  786. switch status {
  787. case types.POWER_STATUS_ON:
  788. return baremetalstatus.RUNNING
  789. case types.POWER_STATUS_OFF:
  790. return baremetalstatus.READY
  791. }
  792. return baremetalstatus.UNKNOWN
  793. }
  794. func PowerStatusToServerStatus(bm *SBaremetalInstance, status types.PowerStatus) string {
  795. switch status {
  796. case types.POWER_STATUS_ON:
  797. if conf, _ := bm.GetSSHConfig(); conf == nil {
  798. return baremetalstatus.SERVER_RUNNING
  799. } else {
  800. if !bm.HasBMC() && !bm.IsMaintenance() {
  801. return baremetalstatus.SERVER_READY
  802. } else {
  803. return baremetalstatus.SERVER_ADMIN
  804. }
  805. }
  806. case types.POWER_STATUS_OFF:
  807. return baremetalstatus.SERVER_READY
  808. }
  809. return baremetalstatus.UNKNOWN
  810. }
  811. func (b *SBaremetalInstance) AutoSyncStatus(ctx context.Context) {
  812. b.SyncStatus(ctx, "", "")
  813. }
  814. func (b *SBaremetalInstance) SyncStatus(ctx context.Context, status string, reason string) {
  815. if status == "" {
  816. powerStatus, err := b.GetPowerStatus()
  817. if err != nil {
  818. log.Errorf("Get power status error: %v", err)
  819. }
  820. status = PowerStatusToBaremetalStatus(powerStatus)
  821. }
  822. b.desc.Set("status", jsonutils.NewString(status))
  823. b.AutoSaveDesc(ctx)
  824. params := jsonutils.NewDict()
  825. params.Add(jsonutils.NewString(status), "status")
  826. if reason != "" {
  827. params.Add(jsonutils.NewString(reason), "reason")
  828. }
  829. _, err := modules.Hosts.PerformAction(b.GetClientSession(), b.GetId(), "status", params)
  830. if err != nil {
  831. log.Errorf("Update baremetal %s status %s error: %v", b.GetId(), status, err)
  832. return
  833. }
  834. log.Infof("Update baremetal %s to status %s", b.GetId(), status)
  835. }
  836. func (b *SBaremetalInstance) AutoSyncAllStatus(ctx context.Context) {
  837. b.SyncAllStatus(ctx, "")
  838. }
  839. func (b *SBaremetalInstance) DelayedSyncStatus(ctx context.Context, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  840. b.AutoSyncAllStatus(ctx)
  841. return nil, nil
  842. }
  843. func (b *SBaremetalInstance) SyncAllStatus(ctx context.Context, status types.PowerStatus) {
  844. var err error
  845. if status == "" {
  846. status, err = b.GetPowerStatus()
  847. if err != nil {
  848. log.Errorf("Get power status error: %v", err)
  849. }
  850. }
  851. b.SyncStatus(ctx, PowerStatusToBaremetalStatus(status), "SyncAllStatus by baremetal-agent")
  852. b.SyncServerStatus(status)
  853. }
  854. func (b *SBaremetalInstance) SyncServerStatus(powerStatus types.PowerStatus) {
  855. if b.GetServerId() == "" {
  856. return
  857. }
  858. status := PowerStatusToServerStatus(b, powerStatus)
  859. if powerStatus == "" {
  860. powerStatus, err := b.GetPowerStatus()
  861. if err != nil {
  862. log.Errorf("Get power status error: %v", err)
  863. }
  864. status = PowerStatusToServerStatus(b, powerStatus)
  865. }
  866. params := &api.ServerPerformStatusInput{}
  867. params.Status = status
  868. params.PowerStates = string(powerStatus)
  869. _, err := modules.Servers.PerformAction(b.GetClientSession(), b.GetServerId(), "status", jsonutils.Marshal(params))
  870. if err != nil {
  871. log.Errorf("Update server %s status %s error: %v", b.GetServerName(), status, err)
  872. return
  873. }
  874. log.Infof("Update server %s to status %s", b.GetServerName(), status)
  875. }
  876. func (b *SBaremetalInstance) GetNics() []types.SNic {
  877. nics := []types.SNic{}
  878. err := b.desc.Unmarshal(&nics, "nic_info")
  879. if err != nil {
  880. log.Errorf("Unmarshal desc to get nics error: %v", err)
  881. return nil
  882. }
  883. return nics
  884. }
  885. func (b *SBaremetalInstance) getNicByType(nicType compute.TNicType) *types.SNic {
  886. nics := b.GetNics()
  887. if len(nics) == 0 {
  888. return nil
  889. }
  890. for i := range nics {
  891. if nics[i].Type == nicType {
  892. return &nics[i]
  893. }
  894. }
  895. return nil
  896. }
  897. func (b *SBaremetalInstance) GetNicByMac(mac net.HardwareAddr) *types.SNic {
  898. nics := b.GetNics()
  899. if len(nics) == 0 {
  900. return nil
  901. }
  902. for _, nic := range nics {
  903. tmp := nic
  904. if tmp.Mac == mac.String() {
  905. return &tmp
  906. }
  907. }
  908. return nil
  909. }
  910. func (b *SBaremetalInstance) GetAdminNic() *types.SNic {
  911. return b.getNicByType(api.NIC_TYPE_ADMIN)
  912. }
  913. func (b *SBaremetalInstance) NeedPXEBoot() bool {
  914. task := b.GetTask()
  915. taskName := "nil"
  916. serverId := b.GetServerId()
  917. if task != nil {
  918. taskName = task.GetName()
  919. }
  920. taskNeedPXEBoot := false
  921. if task != nil && task.NeedPXEBoot() {
  922. taskNeedPXEBoot = true
  923. }
  924. ret := false
  925. reason := ""
  926. if taskNeedPXEBoot {
  927. reason = fmt.Sprintf("Task %s need PXE boot", taskName)
  928. }
  929. emptyBmNoTask := false
  930. if task == nil && len(serverId) == 0 && b.GetHostType() == "baremetal" {
  931. emptyBmNoTask = true
  932. reason = fmt.Sprintf("Baremetal %s is empty and no task executing", b.GetName())
  933. }
  934. noTaskInMaintenance := false
  935. if task == nil && b.IsMaintenance() {
  936. noTaskInMaintenance = true
  937. reason = fmt.Sprintf("Baremetal %s no task executing but in maintenance", b.GetName())
  938. }
  939. if taskNeedPXEBoot || emptyBmNoTask || noTaskInMaintenance {
  940. ret = true
  941. }
  942. log.Infof("Check task %s, server %s NeedPXEBoot: %v(%s)", taskName, serverId, ret, reason)
  943. return ret
  944. }
  945. func (b *SBaremetalInstance) IsMaintenance() bool {
  946. isMt, _ := b.desc.Bool("is_maintenance")
  947. return isMt
  948. }
  949. func (b *SBaremetalInstance) GetArch() (string, error) {
  950. cpuArch, err := b.desc.GetString("cpu_architecture")
  951. if err != nil {
  952. return "", errors.Wrap(err, "Get cpu_architecture from desc")
  953. }
  954. if cpuArch == "" {
  955. return "", errors.Errorf("cpu_architecture is empty")
  956. }
  957. return cpuArch, nil
  958. }
  959. func (b *SBaremetalInstance) GetHostType() string {
  960. hostType, _ := b.desc.GetString("host_type")
  961. return hostType
  962. }
  963. func (b *SBaremetalInstance) GetIPMINic(cliMac net.HardwareAddr) *types.SNic {
  964. nic := b.getNicByType(api.NIC_TYPE_IPMI)
  965. if nic == nil {
  966. return nil
  967. }
  968. if nic.Mac == cliMac.String() {
  969. return nic
  970. }
  971. return nil
  972. }
  973. func (b *SBaremetalInstance) GetIPMINicIPAddr() string {
  974. nic := b.getNicByType(api.NIC_TYPE_IPMI)
  975. if nic == nil {
  976. return ""
  977. }
  978. return nic.IpAddr
  979. }
  980. func (b *SBaremetalInstance) GetDHCPConfig(cliMac net.HardwareAddr) (*dhcp.ResponseConfig, error) {
  981. var nic *types.SNic
  982. var hostname string
  983. var osName string
  984. if b.GetServer() != nil && (b.GetTask() == nil || !b.GetTask().NeedPXEBoot()) {
  985. nic = b.GetServer().GetNicByMac(cliMac)
  986. hostname = b.GetServer().GetHostName()
  987. osName = b.GetServer().GetOsName()
  988. } else {
  989. nic = b.GetNicByMac(cliMac)
  990. }
  991. if nic == nil {
  992. return nil, fmt.Errorf("GetNicDHCPConfig no nic found by mac: %s", cliMac)
  993. }
  994. return b.getDHCPConfig(nic, hostname, false, 0, osName)
  995. }
  996. func (b *SBaremetalInstance) GetPXEDHCPConfig(arch uint16) (*dhcp.ResponseConfig, error) {
  997. return b.getDHCPConfig(b.GetAdminNic(), "", true, arch, "Linux")
  998. }
  999. func (b *SBaremetalInstance) getDHCPConfig(
  1000. nic *types.SNic,
  1001. hostName string,
  1002. isPxe bool,
  1003. arch uint16,
  1004. osName string,
  1005. ) (*dhcp.ResponseConfig, error) {
  1006. if hostName == "" {
  1007. hostName = b.GetName()
  1008. }
  1009. if osName == "" {
  1010. osName = "Linux"
  1011. }
  1012. serverIP, err := b.manager.Agent.GetDHCPServerIP()
  1013. if err != nil {
  1014. return nil, err
  1015. }
  1016. if o.Options.BootLoader == o.BOOT_LOADER_SYSLINUX && arch != dhcp.CLIENT_ARCH_EFI_ARM64 {
  1017. if isPxe && dhcp.IsUEFIPxeArch(arch) && !b.NeedPXEBoot() {
  1018. // TODO: use chainloader boot UEFI firmware,
  1019. // currently not response PXE request,
  1020. // and let BIOS detect bootable device
  1021. b.ClearSSHConfig()
  1022. return nil, errors.Errorf("Baremetal %s not need UEFI PXE boot", b.GetName())
  1023. }
  1024. }
  1025. return GetNicDHCPConfig(nic, serverIP, b.manager.Agent.ListenInterface.HardwareAddr, hostName, isPxe, arch, osName)
  1026. }
  1027. func (b *SBaremetalInstance) GetNotifyUrl() string {
  1028. return fmt.Sprintf("%s/baremetals/%s/notify", b.manager.Agent.GetListenUri(), b.GetId())
  1029. }
  1030. func (b *SBaremetalInstance) getHTTPEndpoint() (string, error) {
  1031. serverIP, err := b.manager.Agent.GetDHCPServerIP()
  1032. if err != nil {
  1033. return "", errors.Wrap(err, "GetDHCPServerIP")
  1034. }
  1035. return fmt.Sprintf("%s:%d", serverIP, o.Options.Port+1000), nil
  1036. }
  1037. func (b *SBaremetalInstance) getHTTPFileUrl(filename string) string {
  1038. endpoint, err := b.getHTTPEndpoint()
  1039. if err != nil {
  1040. log.Errorf("Get http file server endpoint: %v", err)
  1041. return filename
  1042. }
  1043. return fmt.Sprintf("http://%s/tftp/%s", endpoint, filename)
  1044. }
  1045. func (b *SBaremetalInstance) GetImageUrl(disableImageCache bool) string {
  1046. if disableImageCache {
  1047. url, err := b.GetPublicClientSession().GetServiceURL(apis.SERVICE_TYPE_IMAGE, apiidenty.EndpointInterfacePublic, httputils.GET)
  1048. if err != nil {
  1049. log.Errorf("Get image public url: %v", err)
  1050. return ""
  1051. }
  1052. return url
  1053. }
  1054. serverIP, err := b.manager.Agent.GetDHCPServerIP()
  1055. if err != nil {
  1056. log.Errorf("Get http file server: %v", err)
  1057. return ""
  1058. }
  1059. // no /images/, rootcreate.sh will add this
  1060. return fmt.Sprintf("http://%s:%d", serverIP, o.Options.Port+1000)
  1061. }
  1062. func (b *SBaremetalInstance) getBootIsoUrl() string {
  1063. serverIP, err := b.manager.Agent.GetDHCPServerIP()
  1064. if err != nil {
  1065. log.Errorf("Get http file server: %v", err)
  1066. return ""
  1067. }
  1068. // no /images/, rootcreate.sh will add this
  1069. return fmt.Sprintf("http://%s:%d/bootiso/%s.iso", serverIP, o.Options.Port+1000, b.GetId())
  1070. }
  1071. func (b *SBaremetalInstance) GetTFTPResponse() string {
  1072. arch, err := b.GetArch()
  1073. if err != nil {
  1074. log.Errorf("get arch error: %v", err)
  1075. }
  1076. if o.Options.BootLoader == o.BOOT_LOADER_SYSLINUX && arch != apis.OS_ARCH_AARCH64 {
  1077. return b.getSyslinuxConf(true)
  1078. }
  1079. return b.getGrubPXEConf(true)
  1080. }
  1081. func (b *SBaremetalInstance) getIsolinuxConf() string {
  1082. return b.getSyslinuxConf(false)
  1083. }
  1084. func (b *SBaremetalInstance) getSyslinuxPath(filename string, isTftp bool) string {
  1085. if isTftp {
  1086. return b.getHTTPFileUrl(filename)
  1087. } else {
  1088. return filename
  1089. }
  1090. }
  1091. func (b *SBaremetalInstance) findAccessNetwork(accessIp string) (*types.SNetworkConfig, error) {
  1092. params := jsonutils.NewDict()
  1093. params.Add(jsonutils.NewString(accessIp), "ip")
  1094. params.Add(jsonutils.NewString("system"), "scope")
  1095. params.Add(jsonutils.JSONTrue, "is_classic")
  1096. session := b.manager.GetClientSession()
  1097. ret, err := modules.Networks.List(session, params)
  1098. if err != nil {
  1099. return nil, err
  1100. }
  1101. if len(ret.Data) == 0 {
  1102. return nil, errors.Wrapf(httperrors.ErrNotFound, "accessIp %s", accessIp)
  1103. }
  1104. network := types.SNetworkConfig{}
  1105. err = ret.Data[0].Unmarshal(&network)
  1106. return &network, err
  1107. }
  1108. func (b *SBaremetalInstance) getKernelArgs(isTftp bool, initramfs string) string {
  1109. args := []string{
  1110. fmt.Sprintf("token=%s", auth.GetTokenString()),
  1111. fmt.Sprintf("url=%s", b.GetNotifyUrl()),
  1112. }
  1113. bootmode := api.BOOT_MODE_PXE
  1114. if !isTftp {
  1115. adminNic := b.GetAdminNic()
  1116. var mac string
  1117. var addr string
  1118. var mask string
  1119. var gateway string
  1120. if adminNic != nil {
  1121. mac = adminNic.GetMac().String()
  1122. addr = adminNic.IpAddr
  1123. mask = adminNic.GetNetMask()
  1124. gateway = adminNic.Gateway
  1125. } else {
  1126. accessIp := b.GetAccessIp()
  1127. accessNet, _ := b.findAccessNetwork(accessIp)
  1128. if accessNet != nil {
  1129. addr = accessIp
  1130. mask = netutils.Masklen2Mask(int8(accessNet.GuestIpMask)).String()
  1131. gateway = accessNet.GuestGateway
  1132. }
  1133. }
  1134. serverIP, _ := b.manager.Agent.GetDHCPServerIP()
  1135. args = append(args, fmt.Sprintf("mac=%s", mac))
  1136. args = append(args, fmt.Sprintf("dest=%s", serverIP))
  1137. args = append(args, fmt.Sprintf("gateway=%s", gateway))
  1138. args = append(args, fmt.Sprintf("addr=%s", addr))
  1139. args = append(args, fmt.Sprintf("mask=%s", mask))
  1140. bootmode = api.BOOT_MODE_ISO
  1141. }
  1142. args = append(args, fmt.Sprintf("bootmod=%s", bootmode))
  1143. return strings.Join(args, " ")
  1144. }
  1145. func (b *SBaremetalInstance) getGrubPXEConf(isTftp bool) string {
  1146. arch, err := b.GetArch()
  1147. if err != nil {
  1148. log.Warningf("Get baremetal %s architecture error: %v", b.GetName(), err)
  1149. arch = apis.OS_ARCH_X86_64
  1150. }
  1151. kernel := "kernel"
  1152. initrd := "initramfs"
  1153. if arch == apis.OS_ARCH_AARCH64 {
  1154. kernel = "kernel_aarch64"
  1155. initrd = "initramfs_aarch64"
  1156. }
  1157. // TODO: support not tftp situation
  1158. kernelArgs := b.getKernelArgs(isTftp, initrd)
  1159. if len(o.Options.NfsBootRootfs) > 0 {
  1160. kernelArgs = fmt.Sprintf("root=/dev/nfs nfsroot=%s rw", o.Options.NfsBootRootfs)
  1161. }
  1162. var resp string
  1163. endpoint, err := b.getHTTPEndpoint()
  1164. if err != nil {
  1165. log.Fatalf("getHTTPEndpoint %s", err)
  1166. }
  1167. if b.NeedPXEBoot() {
  1168. resp = grub.GetYunionOSConfig(3, endpoint, kernel, kernelArgs, initrd, o.Options.EnableGrubTftpDownload)
  1169. } else {
  1170. resp = grub.GetAutoFindConfig()
  1171. b.ClearSSHConfig()
  1172. }
  1173. return resp
  1174. }
  1175. func (b *SBaremetalInstance) getSyslinuxConf(isTftp bool) string {
  1176. resp := `DEFAULT start
  1177. serial 1 115200
  1178. LABEL start
  1179. MENU LABEL ^Start
  1180. MENU default
  1181. `
  1182. if b.NeedPXEBoot() {
  1183. kernel := "vmlinuz"
  1184. initramfs := "initrd.img"
  1185. if isTftp {
  1186. kernel = b.getHTTPFileUrl("kernel")
  1187. initramfs = b.getHTTPFileUrl("initramfs")
  1188. }
  1189. resp += fmt.Sprintf(" kernel %s\n", kernel)
  1190. args := []string{
  1191. fmt.Sprintf("initrd=%s", initramfs),
  1192. fmt.Sprintf("token=%s", auth.GetTokenString()),
  1193. fmt.Sprintf("url=%s", b.GetNotifyUrl()),
  1194. }
  1195. bootmode := api.BOOT_MODE_PXE
  1196. if !isTftp {
  1197. adminNic := b.GetAdminNic()
  1198. var mac string
  1199. var addr string
  1200. var mask string
  1201. var gateway string
  1202. if adminNic != nil {
  1203. mac = adminNic.GetMac().String()
  1204. addr = adminNic.IpAddr
  1205. mask = adminNic.GetNetMask()
  1206. gateway = adminNic.Gateway
  1207. } else {
  1208. accessIp := b.GetAccessIp()
  1209. accessNet, _ := b.findAccessNetwork(accessIp)
  1210. if accessNet != nil {
  1211. addr = accessIp
  1212. mask = netutils.Masklen2Mask(int8(accessNet.GuestIpMask)).String()
  1213. gateway = accessNet.GuestGateway
  1214. }
  1215. }
  1216. serverIP, _ := b.manager.Agent.GetDHCPServerIP()
  1217. args = append(args, fmt.Sprintf("mac=%s", mac))
  1218. args = append(args, fmt.Sprintf("dest=%s", serverIP))
  1219. args = append(args, fmt.Sprintf("gateway=%s", gateway))
  1220. args = append(args, fmt.Sprintf("addr=%s", addr))
  1221. args = append(args, fmt.Sprintf("mask=%s", mask))
  1222. bootmode = api.BOOT_MODE_ISO
  1223. }
  1224. args = append(args, fmt.Sprintf("bootmod=%s", bootmode))
  1225. resp += fmt.Sprintf(" append %s\n", strings.Join(args, " "))
  1226. } else {
  1227. resp += fmt.Sprintf(" COM32 %s\n", b.getSyslinuxPath("chain.c32", isTftp))
  1228. resp += " APPEND hd0 0\n"
  1229. b.ClearSSHConfig()
  1230. }
  1231. // log.Debugf("[SysLinux config]: \n%s", resp)
  1232. return resp
  1233. }
  1234. func (b *SBaremetalInstance) GetTaskQueue() *tasks.TaskQueue {
  1235. return b.taskQueue
  1236. }
  1237. func (b *SBaremetalInstance) GetTask() tasks.ITask {
  1238. return b.taskQueue.GetTask()
  1239. }
  1240. func (b *SBaremetalInstance) SetTask(task tasks.ITask) {
  1241. // hack: clear exist tasks if task is server destroy task
  1242. if task.GetName() == tasks.BAREMETAL_SERVER_DESTROY_TASK {
  1243. log.Infof("clear tasks of baremetal %s before executing %s", b.GetName(), task.GetName())
  1244. b.taskQueue.ClearTasks()
  1245. }
  1246. b.taskQueue.AppendTask(task)
  1247. if reflect.DeepEqual(task, b.taskQueue.GetTask()) {
  1248. log.Infof("Execute task %s of baremetal %s", task.GetName(), b.GetName())
  1249. tasks.ExecuteTask(task, nil)
  1250. } else {
  1251. log.Warningf("Append task %s of baremetal %s before executing %s", task.GetName(), b.GetName(), b.taskQueue.DebugString())
  1252. }
  1253. }
  1254. func (b *SBaremetalInstance) InitAdminNetif(
  1255. ctx context.Context,
  1256. cliMac net.HardwareAddr,
  1257. wireId string,
  1258. nicType compute.TNicType,
  1259. netType api.TNetworkType,
  1260. isDoImport bool,
  1261. importIpAddr string,
  1262. ) error {
  1263. // start prepare task
  1264. // sync status to PREPARE
  1265. if !isDoImport && nicType == api.NIC_TYPE_ADMIN &&
  1266. utils.IsInStringArray(b.GetStatus(),
  1267. []string{baremetalstatus.INIT,
  1268. baremetalstatus.PREPARE,
  1269. baremetalstatus.PREPARE_FAIL,
  1270. baremetalstatus.UNKNOWN}) &&
  1271. b.GetTask() == nil && b.GetServer() == nil {
  1272. b.SetTask(tasks.NewBaremetalServerPrepareTask(b))
  1273. b.SyncStatus(ctx, baremetalstatus.PREPARE, "")
  1274. }
  1275. nic := b.GetNicByMac(cliMac)
  1276. if nic == nil || nic.WireId == "" {
  1277. _, err := b.attachWire(cliMac, wireId, nicType)
  1278. if err != nil {
  1279. return err
  1280. }
  1281. return b.postAttachWire(ctx, cliMac, nicType, netType, importIpAddr)
  1282. } else if nic.IpAddr == "" {
  1283. return b.postAttachWire(ctx, cliMac, nicType, netType, importIpAddr)
  1284. }
  1285. return nil
  1286. }
  1287. func (b *SBaremetalInstance) RegisterNetif(ctx context.Context, cliMac net.HardwareAddr, wireId string) error {
  1288. var nicType compute.TNicType
  1289. nic := b.GetNicByMac(cliMac)
  1290. if nic != nil {
  1291. nicType = nic.Type
  1292. }
  1293. if nic == nil || nic.WireId == "" || nic.WireId != wireId {
  1294. desc, err := b.attachWire(cliMac, wireId, nicType)
  1295. if err != nil {
  1296. return err
  1297. }
  1298. return b.SaveDesc(ctx, desc)
  1299. }
  1300. return nil
  1301. }
  1302. func (b *SBaremetalInstance) attachWire(mac net.HardwareAddr, wireId string, nicType compute.TNicType) (jsonutils.JSONObject, error) {
  1303. session := b.manager.GetClientSession()
  1304. params := jsonutils.NewDict()
  1305. params.Add(jsonutils.NewString(mac.String()), "mac")
  1306. if nicType != "" {
  1307. params.Add(jsonutils.NewString(string(nicType)), "nic_type")
  1308. }
  1309. params.Add(jsonutils.NewString(wireId), "wire")
  1310. params.Add(jsonutils.NewInt(-1), "index")
  1311. params.Add(jsonutils.JSONTrue, "link_up")
  1312. return modules.Hosts.PerformAction(session, b.GetId(), "add-netif", params)
  1313. }
  1314. func (b *SBaremetalInstance) postAttachWire(ctx context.Context, mac net.HardwareAddr, nicType compute.TNicType, netType api.TNetworkType, ipAddr string) error {
  1315. if ipAddr == "" {
  1316. switch nicType {
  1317. case api.NIC_TYPE_IPMI:
  1318. oldIPMIConf := b.GetRawIPMIConfig()
  1319. if oldIPMIConf != nil && oldIPMIConf.IpAddr != "" {
  1320. ipAddr = oldIPMIConf.IpAddr
  1321. }
  1322. case api.NIC_TYPE_ADMIN:
  1323. accessIp := b.GetAccessIp()
  1324. if accessIp != "" {
  1325. ipAddr = accessIp
  1326. }
  1327. }
  1328. }
  1329. desc, err := b.enableWire(mac, ipAddr, nicType, netType)
  1330. if err != nil {
  1331. return err
  1332. }
  1333. return b.SaveDesc(ctx, desc)
  1334. }
  1335. func (b *SBaremetalInstance) enableWire(mac net.HardwareAddr, ipAddr string, nicType compute.TNicType, netType api.TNetworkType) (jsonutils.JSONObject, error) {
  1336. session := b.manager.GetClientSession()
  1337. params := jsonutils.NewDict()
  1338. params.Add(jsonutils.NewString(mac.String()), "mac")
  1339. if nicType != "" {
  1340. params.Add(jsonutils.NewString(string(nicType)), "nic_type")
  1341. }
  1342. if ipAddr != "" {
  1343. params.Add(jsonutils.NewString(ipAddr), "ip_addr")
  1344. params.Add(jsonutils.JSONTrue, "reserve")
  1345. }
  1346. if nicType == api.NIC_TYPE_IPMI {
  1347. params.Add(jsonutils.NewString("stepup"), "alloc_dir") // alloc bottom up
  1348. }
  1349. if len(netType) > 0 {
  1350. params.Add(jsonutils.NewString(string(netType)), "net_type")
  1351. }
  1352. log.Infof("enable net if params: %s", params.String())
  1353. return modules.Hosts.PerformAction(session, b.GetId(), "enable-netif", params)
  1354. }
  1355. func (b *SBaremetalInstance) GetIPMIConfig() *types.SIPMIInfo {
  1356. conf := b.GetRawIPMIConfig()
  1357. if conf == nil {
  1358. log.Debugf("GetIPMIConfig conf is nil")
  1359. return nil
  1360. }
  1361. if conf.Password == "" {
  1362. log.Debugf("GetIPMIConfig password is nil")
  1363. return nil
  1364. }
  1365. if conf.Username == "" && b.profile != nil {
  1366. conf.Username = b.profile.RootName
  1367. }
  1368. if conf.IpAddr == "" {
  1369. nicIPAddr := b.GetIPMINicIPAddr()
  1370. if nicIPAddr != "" {
  1371. conf.IpAddr = nicIPAddr
  1372. }
  1373. }
  1374. conf.Password = utils.Unquote(conf.Password) // XXX: remove quotes!!!
  1375. if conf.IpAddr == "" {
  1376. log.Debugf("GetIPMIConfig ipaddr s nil")
  1377. return nil
  1378. }
  1379. return conf
  1380. }
  1381. func (b *SBaremetalInstance) GetRawIPMIConfig() *types.SIPMIInfo {
  1382. ipmiInfo := types.SIPMIInfo{}
  1383. err := b.desc.Unmarshal(&ipmiInfo, "ipmi_info")
  1384. if err != nil {
  1385. log.Errorf("Unmarshal IPMIInfo error: %v", err)
  1386. return nil
  1387. }
  1388. if ipmiInfo.Password != "" {
  1389. ipmiInfo.Password, err = utils.DescryptAESBase64(b.GetId(), ipmiInfo.Password)
  1390. if err != nil {
  1391. log.Errorf("DescryptAESBase64 IPMI password error: %v", err)
  1392. return nil
  1393. }
  1394. }
  1395. return &ipmiInfo
  1396. }
  1397. func (b *SBaremetalInstance) GetUEFIInfo() (*types.EFIBootMgrInfo, error) {
  1398. key := "uefi_info"
  1399. if !b.desc.Contains(key) {
  1400. return nil, nil
  1401. }
  1402. info := new(types.EFIBootMgrInfo)
  1403. if err := b.desc.Unmarshal(info, key); err != nil {
  1404. return nil, errors.Wrap(err, "Unmarshal json")
  1405. }
  1406. return info, nil
  1407. }
  1408. func (b *SBaremetalInstance) GetAccessIp() string {
  1409. accessIp, _ := b.desc.GetString("access_ip")
  1410. return accessIp
  1411. }
  1412. func (b *SBaremetalInstance) GetAccessMac() string {
  1413. accessMac, _ := b.desc.GetString("access_mac")
  1414. return accessMac
  1415. }
  1416. func (b *SBaremetalInstance) GetServer() baremetaltypes.IBaremetalServer {
  1417. b.serverLock.Lock()
  1418. defer b.serverLock.Unlock()
  1419. if !b.desc.Contains("server_id") && b.server != nil {
  1420. log.Warningf("baremetal %s server_id not present, remove server %q", b.GetName(), b.server.GetName())
  1421. b.removeServer()
  1422. return nil
  1423. }
  1424. return b.server
  1425. }
  1426. func (b *SBaremetalInstance) GetServerId() string {
  1427. srv := b.GetServer()
  1428. if srv == nil {
  1429. return ""
  1430. }
  1431. return srv.GetId()
  1432. }
  1433. func (b *SBaremetalInstance) GetServerName() string {
  1434. srv := b.GetServer()
  1435. if srv == nil {
  1436. return ""
  1437. }
  1438. return srv.GetName()
  1439. }
  1440. func (b *SBaremetalInstance) RemoveServer() {
  1441. b.serverLock.Lock()
  1442. defer b.serverLock.Unlock()
  1443. b.removeServer()
  1444. }
  1445. func (b *SBaremetalInstance) removeServer() {
  1446. if b.server != nil {
  1447. b.server.RemoveDesc()
  1448. b.server = nil
  1449. }
  1450. }
  1451. func (b *SBaremetalInstance) SetExistingIPMIIPAddr(ipAddr string) {
  1452. info, _ := b.desc.Get("ipmi_info")
  1453. if info == nil {
  1454. info = jsonutils.NewDict()
  1455. }
  1456. oIPAddr, _ := info.GetString("ip_addr")
  1457. if oIPAddr == "" {
  1458. info.(*jsonutils.JSONDict).Add(jsonutils.NewString(ipAddr), "ip_addr")
  1459. }
  1460. b.desc.Set("ipmi_info", info)
  1461. }
  1462. func (b *SBaremetalInstance) HasBMC() bool {
  1463. conf := b.GetIPMIConfig()
  1464. if conf == nil {
  1465. return false
  1466. }
  1467. return true
  1468. }
  1469. func (b *SBaremetalInstance) GetHostSSHClient() (*ssh.Client, error) {
  1470. conf, err := b.GetSSHConfig()
  1471. if err != nil {
  1472. return nil, errors.Wrap(err, "Get host ssh config")
  1473. }
  1474. if conf == nil {
  1475. return nil, errors.Errorf("Host ssh config is empty")
  1476. }
  1477. sshCli, err := ssh.NewClient(conf.RemoteIP, 22, "root", conf.Password, "")
  1478. if err != nil {
  1479. return nil, errors.Wrap(err, "New ssh client")
  1480. }
  1481. return sshCli, nil
  1482. }
  1483. func (b *SBaremetalInstance) GetServerSSHClient() (*ssh.Client, error) {
  1484. s := b.GetServer()
  1485. if s == nil {
  1486. return nil, errors.Error("No server")
  1487. }
  1488. privateKey, err := modules.Sshkeypairs.FetchPrivateKey(context.TODO(), auth.AdminCredential())
  1489. if err != nil {
  1490. return nil, errors.Wrapf(err, "Get server %s login info", s.GetId())
  1491. }
  1492. nics := s.GetNics()
  1493. var errs []error
  1494. for idx, nic := range nics {
  1495. if nic.Ip != "" {
  1496. for _, user := range []string{"cloudroot", "root"} {
  1497. sshCli, err := ssh.NewClient(nic.Ip, 22, user, "", privateKey)
  1498. if err != nil {
  1499. err = errors.Wrapf(err, "New server %s ssh client %s@%s", s.GetName(), user, nic.Ip)
  1500. errs = append(errs, err)
  1501. } else {
  1502. return sshCli, nil
  1503. }
  1504. }
  1505. } else {
  1506. errs = append(errs, errors.Errorf("nic %d link_up: %v, ip: %q", idx, nic.LinkUp, nic.Ip))
  1507. }
  1508. }
  1509. return nil, errors.NewAggregate(errs)
  1510. }
  1511. func (b *SBaremetalInstance) SSHReachable() (bool, error) {
  1512. var errs []error
  1513. if cli, err := b.GetHostSSHClient(); err != nil {
  1514. errs = append(errs, err)
  1515. } else {
  1516. // host ssh reachable
  1517. cli.Close()
  1518. return true, nil
  1519. }
  1520. if cli, err := b.GetServerSSHClient(); err != nil {
  1521. errs = append(errs, err)
  1522. } else {
  1523. // server ssh reachable
  1524. cli.Close()
  1525. return true, nil
  1526. }
  1527. return false, errors.NewAggregate(errs)
  1528. }
  1529. func (b *SBaremetalInstance) sshRunWrapper(
  1530. hostRun func(hostCli *ssh.Client) ([]string, error),
  1531. serverRun func(serverCli *ssh.Client) ([]string, error),
  1532. ) ([]string, error) {
  1533. hostCli, err := b.GetHostSSHClient()
  1534. if err != nil {
  1535. log.Warningf("Get host ssh client error: %v", err)
  1536. } else {
  1537. if hostCli != nil {
  1538. return hostRun(hostCli)
  1539. }
  1540. }
  1541. serverCli, err := b.GetServerSSHClient()
  1542. if err != nil {
  1543. return nil, errors.Wrapf(err, "Get baremetal %s server ssh client", b.GetName())
  1544. }
  1545. return serverRun(serverCli)
  1546. }
  1547. func (b *SBaremetalInstance) sshRun(hostCmd string, serverCmd string) ([]string, error) {
  1548. return b.sshRunWrapper(
  1549. func(hostCli *ssh.Client) ([]string, error) {
  1550. return hostCli.RawRun(hostCmd)
  1551. },
  1552. func(serverCli *ssh.Client) ([]string, error) {
  1553. return serverCli.RunWithTTY(serverCmd)
  1554. },
  1555. )
  1556. }
  1557. func (b *SBaremetalInstance) adjustUEFIWrapper(ctx context.Context, cli *ssh.Client, f func() error) error {
  1558. isUEFI, err := uefi.RemoteIsUEFIBoot(cli)
  1559. if err != nil {
  1560. return errors.Wrap(err, "Check is uefi boot")
  1561. }
  1562. if !isUEFI {
  1563. return b.CleanUEFIInfo(ctx)
  1564. }
  1565. return f()
  1566. }
  1567. func (b *SBaremetalInstance) AdjustUEFICurrentBootOrder(ctx context.Context, hostCli *ssh.Client) error {
  1568. return b.adjustUEFIWrapper(ctx, hostCli, func() error {
  1569. mgr, err := uefi.NewEFIBootMgrFromRemote(hostCli, false)
  1570. if err != nil {
  1571. return errors.Wrap(err, "NewEFIBootMgrFromRemote")
  1572. }
  1573. if err := uefi.RemoteSetCurrentBootAtFirst(hostCli, mgr); err != nil {
  1574. return errors.Wrap(err, "Set current boot order at first")
  1575. }
  1576. return b.SendUEFIInfo(ctx, mgr)
  1577. })
  1578. }
  1579. func (b *SBaremetalInstance) updateUEFIInfo(ctx context.Context, uefiData jsonutils.JSONObject) error {
  1580. desc := b.desc
  1581. desc.Add(uefiData, "uefi_info")
  1582. if err := b.SaveDesc(ctx, desc); err != nil {
  1583. return errors.Wrap(err, "Save uefi_info")
  1584. }
  1585. updateData := jsonutils.NewDict()
  1586. updateData.Add(uefiData, "uefi_info")
  1587. if _, err := modules.Hosts.Update(b.GetClientSession(), b.GetId(), updateData); err != nil {
  1588. return errors.Wrap(err, "Update cloud uefi info")
  1589. }
  1590. return nil
  1591. }
  1592. func (b *SBaremetalInstance) CleanUEFIInfo(ctx context.Context) error {
  1593. if err := b.updateUEFIInfo(ctx, jsonutils.NewDict()); err != nil {
  1594. return errors.Wrap(err, "CleanUEFIInfo")
  1595. }
  1596. return nil
  1597. }
  1598. func (b *SBaremetalInstance) SendUEFIInfo(ctx context.Context, mgr *uefi.BootMgr) error {
  1599. info, err := mgr.ToEFIBootMgrInfo()
  1600. if err != nil {
  1601. return err
  1602. }
  1603. if err := b.updateUEFIInfo(ctx, jsonutils.Marshal(info)); err != nil {
  1604. return errors.Wrap(err, "SendUEFIInfo")
  1605. }
  1606. return nil
  1607. }
  1608. func (b *SBaremetalInstance) adjustServerUEFIBootOrder(ctx context.Context, srvCli *ssh.Client) error {
  1609. return b.adjustUEFIWrapper(ctx, srvCli, func() error {
  1610. info, err := b.GetUEFIInfo()
  1611. if err != nil {
  1612. return errors.Wrap(err, "GetUEFIInfo from local desc")
  1613. }
  1614. if info == nil {
  1615. return uefi.RemoteTryToSetPXEBoot(srvCli)
  1616. }
  1617. if err := uefi.RemoteTryToSetPXEBoot(srvCli); err != nil {
  1618. log.Warningf("RemoteTryToSetPXEBoot error: %v", err)
  1619. }
  1620. _, err = uefi.RemoteSetBootOrderByInfo(srvCli, info.GetPXEEntry())
  1621. return err
  1622. })
  1623. }
  1624. func (b *SBaremetalInstance) AdjustUEFIBootOrder(ctx context.Context) error {
  1625. _, err := b.sshRunWrapper(
  1626. func(hostCli *ssh.Client) ([]string, error) {
  1627. return nil, b.AdjustUEFICurrentBootOrder(ctx, hostCli)
  1628. },
  1629. func(srvCli *ssh.Client) ([]string, error) {
  1630. return nil, b.adjustServerUEFIBootOrder(ctx, srvCli)
  1631. },
  1632. )
  1633. return err
  1634. }
  1635. func (b *SBaremetalInstance) SSHReboot(ctx context.Context) error {
  1636. if !b.HasBMC() {
  1637. // try adjust uefi boot order before reboot
  1638. if err := b.AdjustUEFIBootOrder(ctx); err != nil {
  1639. return errors.Wrap(err, "Adjust uefi boot order")
  1640. }
  1641. }
  1642. if _, err := b.sshRun("/sbin/reboot", "sudo shutdown -r now && exit"); err != nil {
  1643. if !ssh.IsExitMissingError(err) {
  1644. return errors.Wrap(err, "Try reboot")
  1645. }
  1646. }
  1647. b.ClearSSHConfig()
  1648. return nil
  1649. }
  1650. func (b *SBaremetalInstance) SSHShutdown() error {
  1651. if _, err := b.sshRun("/sbin/poweroff", "sudo shutdown -h now && exit"); err != nil {
  1652. if ssh.IsExitMissingError(err) {
  1653. return nil
  1654. }
  1655. return errors.Wrap(err, "Try poweroff")
  1656. }
  1657. return nil
  1658. }
  1659. func (b *SBaremetalInstance) GetIPMITool() *ipmitool.LanPlusIPMI {
  1660. conf := b.GetIPMIConfig()
  1661. if conf == nil {
  1662. log.Debugf("GetIPMIConfig is nil")
  1663. return nil
  1664. }
  1665. return ipmitool.NewLanPlusIPMI(conf.IpAddr, conf.Username, conf.Password)
  1666. }
  1667. func (b *SBaremetalInstance) isRedfishCapable() bool {
  1668. conf := b.GetIPMIConfig()
  1669. if conf == nil {
  1670. return false
  1671. }
  1672. if !conf.Verified {
  1673. return false
  1674. }
  1675. if !conf.RedfishApi {
  1676. return false
  1677. }
  1678. return true
  1679. }
  1680. func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfishDriver {
  1681. if !b.isRedfishCapable() {
  1682. return nil
  1683. }
  1684. conf := b.GetIPMIConfig()
  1685. var endpoint = "https://" + conf.IpAddr
  1686. if strings.Contains(conf.IpAddr, ":") {
  1687. endpoint = fmt.Sprintf("https://[%s]", conf.IpAddr)
  1688. }
  1689. return redfish.NewRedfishDriver(ctx, endpoint, conf.Username, conf.Password, false)
  1690. }
  1691. func (b *SBaremetalInstance) GetIPMILanChannel() uint8 {
  1692. conf := b.GetIPMIConfig()
  1693. if conf == nil {
  1694. return 0
  1695. }
  1696. return conf.LanChannel
  1697. }
  1698. func (b *SBaremetalInstance) DoPXEBoot() error {
  1699. log.Infof("Do PXE Boot ........., wait")
  1700. b.ClearSSHConfig()
  1701. ipmiCli := b.GetIPMITool()
  1702. if ipmiCli != nil {
  1703. return ipmitool.DoRebootToPXE(ipmiCli)
  1704. }
  1705. return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
  1706. }
  1707. func (b *SBaremetalInstance) doRedfishPowerOn() error {
  1708. log.Infof("Do Redfish PowerOn ........., wait")
  1709. ctx := context.Background()
  1710. b.ClearSSHConfig()
  1711. redfishApi := b.GetRedfishCli(ctx)
  1712. if redfishApi != nil {
  1713. err := redfishApi.Reset(ctx, "On")
  1714. if err != nil {
  1715. if httputils.ErrorCode(err) == 409 {
  1716. return errors.Wrap(err, "redfishApi.Reset On fail, code 409")
  1717. } else {
  1718. return errors.Wrap(err, "redfishApi.Reset On")
  1719. }
  1720. }
  1721. return nil
  1722. }
  1723. return fmt.Errorf("Baremetal %s redfishApi is nil", b.GetId())
  1724. }
  1725. func (b *SBaremetalInstance) DoRedfishPowerOn() error {
  1726. err := b.doRedfishPowerOn()
  1727. if err == nil {
  1728. return nil
  1729. }
  1730. var errs = []error{err}
  1731. log.Warningf("Do redfish power on error: %v, try use fallback IPMI way", err)
  1732. ipmiCli := b.GetIPMITool()
  1733. if err := ipmitool.DoReboot(ipmiCli); err != nil {
  1734. errs = append(errs, errors.Wrap(err, "Fallback to use ipmitool reboot"))
  1735. } else {
  1736. return nil
  1737. }
  1738. return errors.NewAggregate(errs)
  1739. }
  1740. /*
  1741. func (b *SBaremetalInstance) DoDiskBoot() error {
  1742. log.Infof("Do DISK Boot ........., wait")
  1743. b.ClearSSHConfig()
  1744. ipmiCli := b.GetIPMITool()
  1745. if ipmiCli != nil {
  1746. return ipmitool.DoRebootToDisk(ipmiCli)
  1747. }
  1748. return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
  1749. }
  1750. */
  1751. func (b *SBaremetalInstance) GetPowerStatus() (types.PowerStatus, error) {
  1752. status, err := b.getPowerStatus()
  1753. if err != nil {
  1754. if errors.Cause(err) != types.ErrIPMIToolNull {
  1755. return "", errors.Wrap(err, "GetPowerStatus")
  1756. } else if b.HasBMC() {
  1757. return "", errors.Wrap(err, "GetPowerStatus from ipmi")
  1758. }
  1759. }
  1760. return status, nil
  1761. }
  1762. func (b *SBaremetalInstance) getPowerStatus() (types.PowerStatus, error) {
  1763. ipmiCli := b.GetIPMITool()
  1764. if ipmiCli == nil {
  1765. if cli, err := b.GetHostSSHClient(); err == nil {
  1766. cli.Close()
  1767. return types.POWER_STATUS_ON, nil
  1768. } else {
  1769. log.Warningf("Use host %s ssh client get powerstatus: %v", b.GetName(), err)
  1770. }
  1771. if cli, err := b.GetServerSSHClient(); err == nil {
  1772. cli.Close()
  1773. b.ClearSSHConfig()
  1774. return types.POWER_STATUS_ON, nil
  1775. } else {
  1776. log.Warningf("Use server %s ssh client get powerstatus: %v", b.GetServerName(), err)
  1777. }
  1778. return "", errors.Wrapf(types.ErrIPMIToolNull, "Baremetal %s", b.GetId())
  1779. }
  1780. cps, err := ipmitool.GetChassisPowerStatus(ipmiCli)
  1781. if err != nil {
  1782. return "", errors.Wrap(err, "ipmitool.GetChassisPowerStatus")
  1783. }
  1784. return types.PowerStatus(cps), nil
  1785. }
  1786. func (b *SBaremetalInstance) DoPowerShutdown(soft bool) error {
  1787. b.ClearSSHConfig()
  1788. ipmiCli := b.GetIPMITool()
  1789. if ipmiCli != nil {
  1790. if soft {
  1791. return ipmitool.DoSoftShutdown(ipmiCli)
  1792. }
  1793. return ipmitool.DoHardShutdown(ipmiCli)
  1794. }
  1795. return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
  1796. }
  1797. func (b *SBaremetalInstance) GetStorageDriver() string {
  1798. driver, _ := b.desc.GetString("storage_driver")
  1799. return driver
  1800. }
  1801. func (b *SBaremetalInstance) GetZoneId() string {
  1802. return b.manager.GetZoneId()
  1803. }
  1804. func (b *SBaremetalInstance) GetZoneName() string {
  1805. return b.manager.GetZoneName()
  1806. }
  1807. func (b *SBaremetalInstance) GetStorageCacheId() string {
  1808. return b.manager.Agent.CacheManager.GetId()
  1809. }
  1810. func (b *SBaremetalInstance) GetBootMode() string {
  1811. bootMode, _ := b.desc.GetString("boot_mode")
  1812. if len(bootMode) == 0 {
  1813. bootMode = api.BOOT_MODE_PXE
  1814. }
  1815. return bootMode
  1816. }
  1817. func (b *SBaremetalInstance) GetRegion() string {
  1818. r, _ := b.desc.GetString("region")
  1819. return r
  1820. }
  1821. func (b *SBaremetalInstance) GetRegionId() string {
  1822. r, _ := b.desc.GetString("region_id")
  1823. return r
  1824. }
  1825. func (b *SBaremetalInstance) GetSerialNumber() string {
  1826. r, _ := b.desc.GetString("sn")
  1827. return r
  1828. }
  1829. func (b *SBaremetalInstance) GetManufacture() string {
  1830. r, _ := b.desc.GetString("sys_info", "manufacture")
  1831. return r
  1832. }
  1833. func (b *SBaremetalInstance) GetModel() string {
  1834. r, _ := b.desc.GetString("sys_info", "model")
  1835. return r
  1836. }
  1837. func (b *SBaremetalInstance) GetNodeCount() string {
  1838. r, _ := b.desc.GetString("node_count")
  1839. return r
  1840. }
  1841. func (b *SBaremetalInstance) GetMemGb() string {
  1842. memMb, _ := b.desc.Int("mem_size")
  1843. return strconv.FormatInt(memMb/1024, 10)
  1844. }
  1845. func (b *SBaremetalInstance) DelayedRemove(_ context.Context, _ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1846. b.remove()
  1847. return nil, nil
  1848. }
  1849. func (b *SBaremetalInstance) remove() {
  1850. b.manager.CleanBaremetal(b.GetId())
  1851. b.manager = nil
  1852. b.desc = nil
  1853. }
  1854. func (b *SBaremetalInstance) StartNewTask(factory tasks.TaskFactory, userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
  1855. go func() {
  1856. task := factory(userCred, b, taskId, data)
  1857. b.SetTask(task)
  1858. }()
  1859. }
  1860. func (b *SBaremetalInstance) StartBaremetalMaintenanceTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
  1861. if jsonutils.QueryBoolean(data, "force_reboot", false) {
  1862. b.ClearSSHConfig()
  1863. }
  1864. if jsonutils.QueryBoolean(data, "guest_running", false) {
  1865. data.(*jsonutils.JSONDict).Set("soft_reboot", jsonutils.JSONTrue)
  1866. }
  1867. b.StartNewTask(tasks.NewBaremetalMaintenanceTask, userCred, taskId, data)
  1868. }
  1869. func (b *SBaremetalInstance) StartBaremetalUnmaintenanceTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
  1870. b.StartNewTask(tasks.NewBaremetalUnmaintenanceTask, userCred, taskId, data)
  1871. }
  1872. func (b *SBaremetalInstance) StartBaremetalReprepareTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
  1873. b.StartNewTask(tasks.NewBaremetalReprepareTask, userCred, taskId, data)
  1874. }
  1875. func (b *SBaremetalInstance) StartBaremetalResetBMCTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1876. b.StartNewTask(tasks.NewBaremetalResetBMCTask, userCred, taskId, data)
  1877. return nil
  1878. }
  1879. func (b *SBaremetalInstance) StartBaremetalIpmiProbeTask(ctx context.Context, userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1880. session := b.manager.GetClientSession()
  1881. data, _ = b.manager.fetchBaremetal(session, b.GetId())
  1882. if err := b.SaveDesc(ctx, data); err != nil {
  1883. return err
  1884. }
  1885. b.StartNewTask(tasks.NewBaremetalIpmiProbeTask, userCred, taskId, data)
  1886. return nil
  1887. }
  1888. func (b *SBaremetalInstance) StartBaremetalCdromTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1889. b.StartNewTask(tasks.NewBaremetalCdromTask, userCred, taskId, data)
  1890. return nil
  1891. }
  1892. func (b *SBaremetalInstance) DelayedServerReset(ctx context.Context, _ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1893. err := b.DoPXEBoot()
  1894. return nil, err
  1895. }
  1896. func (b *SBaremetalInstance) StartServerCreateTask(ctx context.Context, userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1897. b.serverLock.Lock()
  1898. defer b.serverLock.Unlock()
  1899. if b.server != nil {
  1900. return fmt.Errorf("Baremetal %s already have server %s", b.GetName(), b.server.GetName())
  1901. }
  1902. descData, err := data.Get("desc")
  1903. if err != nil {
  1904. return fmt.Errorf("Create data not found server desc: %v", err)
  1905. }
  1906. server, err := newBaremetalServer(b, descData.(*jsonutils.JSONDict))
  1907. if err != nil {
  1908. return fmt.Errorf("New server error: %v", err)
  1909. }
  1910. b.server = server
  1911. b.desc.Set("server_id", jsonutils.NewString(b.server.GetId()))
  1912. if err := b.AutoSaveDesc(ctx); err != nil {
  1913. return err
  1914. }
  1915. b.StartNewTask(tasks.NewBaremetalServerCreateTask, userCred, taskId, data)
  1916. return nil
  1917. }
  1918. func (b *SBaremetalInstance) StartServerDeployTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1919. desc, err := data.Get("desc")
  1920. if err != nil {
  1921. return fmt.Errorf("Not found desc in data")
  1922. }
  1923. if err := b.GetServer().SaveDesc(desc); err != nil {
  1924. return fmt.Errorf("Save server desc: %v", err)
  1925. }
  1926. b.StartNewTask(tasks.NewBaremetalServerDeployTask, userCred, taskId, data)
  1927. return nil
  1928. }
  1929. func (b *SBaremetalInstance) StartServerRebuildTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1930. desc, err := data.Get("desc")
  1931. if err != nil {
  1932. return fmt.Errorf("Not found desc in data")
  1933. }
  1934. if err := b.GetServer().SaveDesc(desc); err != nil {
  1935. return fmt.Errorf("Save server desc: %v", err)
  1936. }
  1937. b.StartNewTask(tasks.NewBaremetalServerRebuildTask, userCred, taskId, data)
  1938. return nil
  1939. }
  1940. func (b *SBaremetalInstance) StartServerStartTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1941. b.StartNewTask(tasks.NewBaremetalServerStartTask, userCred, taskId, data)
  1942. return nil
  1943. }
  1944. func (b *SBaremetalInstance) StartServerStopTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
  1945. b.StartNewTask(tasks.NewBaremetalServerStopTask, userCred, taskId, data)
  1946. return nil
  1947. }
  1948. func (b *SBaremetalInstance) StartServerDestroyTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
  1949. b.StartNewTask(tasks.NewBaremetalServerDestroyTask, userCred, taskId, data)
  1950. }
  1951. func (b *SBaremetalInstance) DelayedSyncIPMIInfo(ctx context.Context, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1952. ipmiCli := b.GetIPMITool()
  1953. lanChannel := b.GetIPMILanChannel()
  1954. sysInfo, err := ipmitool.GetSysInfo(ipmiCli)
  1955. if err != nil {
  1956. return nil, errors.Wrap(err, "GetSysInfo")
  1957. }
  1958. profile, err := profiles.GetProfile(ctx, sysInfo)
  1959. if err != nil {
  1960. return nil, errors.Wrap(err, "GetProfile")
  1961. }
  1962. if lanChannel <= 0 {
  1963. if len(profile.LanChannels) == 0 {
  1964. return nil, errors.Wrap(errors.ErrInvalidStatus, "baremetal profile not valid lan channel?")
  1965. }
  1966. lanChannel = profile.LanChannels[0]
  1967. }
  1968. retObj := make(map[string]string)
  1969. if ipAddr, _ := data.GetString("ip_addr"); ipAddr != "" {
  1970. err = ipmitool.SetLanStaticIP(ipmiCli, lanChannel, ipAddr)
  1971. if err != nil {
  1972. return nil, err
  1973. }
  1974. // TODO: netutils.wait_ip_alive(ipAddr, 120)
  1975. retObj["ipmi_ip_addr"] = ipAddr
  1976. }
  1977. if passwd, _ := data.GetString("password"); passwd != "" {
  1978. err = ipmitool.SetLanPasswd(ipmiCli, profile.RootId, passwd)
  1979. if err != nil {
  1980. return nil, err
  1981. }
  1982. retObj["ipmi_password"] = passwd
  1983. }
  1984. return jsonutils.Marshal(retObj), nil
  1985. }
  1986. func (b *SBaremetalInstance) DelayedSyncDesc(ctx context.Context, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1987. if data == nil {
  1988. session := b.manager.GetClientSession()
  1989. data, _ = b.manager.fetchBaremetal(session, b.GetId())
  1990. }
  1991. err := b.SaveDesc(ctx, data)
  1992. return nil, err
  1993. }
  1994. func (b *SBaremetalInstance) DelayedServerStatus(ctx context.Context, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1995. ps, err := b.GetPowerStatus()
  1996. if err != nil {
  1997. return nil, err
  1998. }
  1999. status := PowerStatusToServerStatus(b, ps)
  2000. resp := jsonutils.NewDict()
  2001. resp.Add(jsonutils.NewString(status), "status")
  2002. return resp, err
  2003. }
  2004. func (b *SBaremetalInstance) SendNicInfo(ctx context.Context, nic *types.SNicDevInfo, idx int, nicType compute.TNicType, reset bool, ipAddr string, reserve bool) error {
  2005. params := jsonutils.NewDict()
  2006. params.Add(jsonutils.NewString(nic.Mac.String()), "mac")
  2007. params.Add(jsonutils.NewInt(int64(nic.Speed)), "rate")
  2008. if idx >= 0 {
  2009. params.Add(jsonutils.NewInt(int64(idx)), "index")
  2010. }
  2011. if nicType != "" {
  2012. params.Add(jsonutils.NewString(string(nicType)), "nic_type")
  2013. }
  2014. params.Add(jsonutils.NewInt(int64(nic.Mtu)), "mtu")
  2015. if nic.Up != nil {
  2016. params.Add(jsonutils.NewBool(*nic.Up), "link_up")
  2017. }
  2018. if reset {
  2019. params.Add(jsonutils.JSONTrue, "reset")
  2020. }
  2021. if ipAddr != "" {
  2022. params.Add(jsonutils.NewString(ipAddr), "ip_addr")
  2023. params.Add(jsonutils.JSONTrue, "require_designated_ip")
  2024. if reserve {
  2025. params.Add(jsonutils.JSONTrue, "reserve")
  2026. }
  2027. }
  2028. localNic := b.GetNicByMac(nic.Mac)
  2029. if localNic != nil {
  2030. params.Add(jsonutils.NewString(localNic.WireId), "wire_id")
  2031. }
  2032. resp, err := modules.Hosts.PerformAction(
  2033. b.GetClientSession(),
  2034. b.GetId(),
  2035. "add-netif",
  2036. params,
  2037. )
  2038. if err != nil {
  2039. return err
  2040. }
  2041. return b.SaveDesc(ctx, resp)
  2042. }
  2043. func bindMount(src, dst string) error {
  2044. err := procutils.NewCommand("touch", dst).Run()
  2045. if err != nil {
  2046. return errors.Wrapf(err, "touch %s", dst)
  2047. }
  2048. err = procutils.NewCommand("mount", "-o", "ro,bind", src, dst).Run()
  2049. if err != nil {
  2050. return errors.Wrapf(err, "mount %s %s", src, dst)
  2051. }
  2052. return nil
  2053. }
  2054. func unbindMount(dst string) error {
  2055. err := procutils.NewCommand("umount", dst).Run()
  2056. if err != nil {
  2057. return errors.Wrapf(err, "umount %s", dst)
  2058. }
  2059. return nil
  2060. }
  2061. func (b *SBaremetalInstance) EnablePxeBoot() bool {
  2062. return jsonutils.QueryBoolean(b.desc, "enable_pxe_boot", true)
  2063. }
  2064. func (b *SBaremetalInstance) GenerateBootISO() error {
  2065. // precheck
  2066. conf := b.GetRawIPMIConfig()
  2067. if !conf.Verified {
  2068. return errors.Error("GenerateBootISO: IPMI not supported")
  2069. }
  2070. if !conf.CdromBoot {
  2071. return errors.Error("GenerateBootISO: cdrom boot not supported")
  2072. }
  2073. accessIp := b.GetAccessIp()
  2074. if accessIp == "" {
  2075. return errors.Error("GenerateBootISO: empty accessIp")
  2076. }
  2077. adminNic := b.GetAdminNic()
  2078. if adminNic == nil {
  2079. accessNet, _ := b.findAccessNetwork(accessIp)
  2080. if accessNet == nil {
  2081. return errors.Error("GenerateBootISO: Nil access Network")
  2082. }
  2083. }
  2084. ctx := context.Background()
  2085. redfishApi := b.GetRedfishCli(ctx)
  2086. if redfishApi == nil {
  2087. return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
  2088. }
  2089. // generate ISO
  2090. isoDir, err := ioutil.TempDir("", "bmiso")
  2091. if err != nil {
  2092. return errors.Wrap(err, "ioutil.TempDir")
  2093. }
  2094. defer os.RemoveAll(isoDir)
  2095. isoLinDir := filepath.Join(isoDir, "isolinux")
  2096. err = os.Mkdir(isoLinDir, os.FileMode(0766))
  2097. if err != nil {
  2098. return errors.Wrapf(err, "Mkdir %s", isoLinDir)
  2099. }
  2100. for _, f := range []string{
  2101. "chain.c32", "ldlinux.c32", "libutil.c32", "libcom32.c32",
  2102. } {
  2103. err = bindMount(filepath.Join(o.Options.TftpRoot, f), filepath.Join(isoLinDir, f))
  2104. if err != nil {
  2105. return errors.Wrapf(err, "Link %s", f)
  2106. }
  2107. defer unbindMount(filepath.Join(isoLinDir, f))
  2108. }
  2109. for src, dst := range map[string]string{
  2110. "kernel": "vmlinuz",
  2111. "initramfs": "initrd.img",
  2112. } {
  2113. err = bindMount(filepath.Join(o.Options.TftpRoot, src), filepath.Join(isoLinDir, dst))
  2114. if err != nil {
  2115. return errors.Wrapf(err, "Link %s %s", src, dst)
  2116. }
  2117. defer unbindMount(filepath.Join(isoLinDir, dst))
  2118. }
  2119. for _, f := range []string{
  2120. "isolinux.bin",
  2121. } {
  2122. err = procutils.NewCommand("cp", filepath.Join(o.Options.TftpRoot, f), filepath.Join(isoLinDir, f)).Run()
  2123. if err != nil {
  2124. return errors.Wrapf(err, "cp %s", f)
  2125. }
  2126. }
  2127. cfgCont := b.getIsolinuxConf()
  2128. err = fileutils2.FilePutContents(filepath.Join(isoLinDir, "isolinux.cfg"), cfgCont, false)
  2129. if err != nil {
  2130. return errors.Wrap(err, "fileutils.FilePutContent")
  2131. }
  2132. args := []string{
  2133. "-quiet",
  2134. "-J", "-R",
  2135. "-input-charset", "utf-8",
  2136. "-b", "isolinux/isolinux.bin",
  2137. "-c", "isolinux/boot.cat",
  2138. "-no-emul-boot",
  2139. "-boot-load-size", "4",
  2140. "-boot-info-table",
  2141. "-o", b.getBootIsoImagePath(),
  2142. isoDir,
  2143. }
  2144. err = procutils.NewCommand("mkisofs", args...).Run()
  2145. if err != nil {
  2146. return errors.Wrap(err, "procutils.NewCommand mkisofs")
  2147. }
  2148. // mount the virtual media
  2149. err = redfish.MountVirtualCdrom(ctx, redfishApi, b.getBootIsoUrl(), true)
  2150. if err != nil {
  2151. return errors.Wrap(err, "redfish.MountVirtualCdrom")
  2152. }
  2153. return nil
  2154. }
  2155. func (b *SBaremetalInstance) clearBootIso() error {
  2156. ctx := context.Background()
  2157. redfishApi := b.GetRedfishCli(ctx)
  2158. if redfishApi == nil {
  2159. return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
  2160. }
  2161. err := redfish.UmountVirtualCdrom(ctx, redfishApi)
  2162. if err != nil {
  2163. return errors.Wrap(err, "redfish.UmountVirtualCdrom")
  2164. }
  2165. return b.clearBootIsoImage()
  2166. }
  2167. func (b *SBaremetalInstance) clearBootIsoImage() error {
  2168. path := b.getBootIsoImagePath()
  2169. if fileutils2.Exists(path) {
  2170. return os.Remove(path)
  2171. }
  2172. return nil
  2173. }
  2174. func (b *SBaremetalInstance) getBootIsoImagePath() string {
  2175. return filepath.Join(o.Options.BootIsoPath, b.GetId()+".iso")
  2176. }
  2177. func (b *SBaremetalInstance) DoNTPConfig() error {
  2178. var urls []string
  2179. for _, ep := range []string{"internal", "public"} {
  2180. urls, _ = auth.GetServiceURLs("ntp", o.Options.Region, "", ep, httputils.POST)
  2181. if len(urls) > 0 {
  2182. break
  2183. }
  2184. }
  2185. if len(urls) == 0 {
  2186. log.Warningf("NO ntp server specified, skip DoNTPConfig")
  2187. return nil
  2188. }
  2189. for i := range urls {
  2190. if strings.HasPrefix(urls[i], "ntp://") {
  2191. urls[i] = urls[i][6:]
  2192. }
  2193. }
  2194. log.Infof("Set NTP %s", urls)
  2195. ntpConf := redfish.SNTPConf{}
  2196. ntpConf.ProtocolEnabled = true
  2197. ntpConf.TimeZone = o.Options.TimeZone
  2198. ntpConf.NTPServers = urls
  2199. ctx := context.Background()
  2200. redfishApi := b.GetRedfishCli(ctx)
  2201. if redfishApi == nil {
  2202. return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
  2203. }
  2204. err := redfishApi.SetNTPConf(ctx, ntpConf)
  2205. if err != nil {
  2206. return errors.Wrap(err, "redfishApi.SetNTPConf")
  2207. }
  2208. return nil
  2209. }
  2210. func (b *SBaremetalInstance) fetchLogs(ctx context.Context, logType string, since time.Time) ([]redfish.SEvent, error) {
  2211. redfishApi := b.GetRedfishCli(ctx)
  2212. if redfishApi == nil {
  2213. // errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
  2214. return nil, nil
  2215. }
  2216. switch logType {
  2217. case redfish.EVENT_TYPE_SYSTEM:
  2218. return redfishApi.ReadSystemLogs(ctx, since)
  2219. case redfish.EVENT_TYPE_MANAGER:
  2220. return redfishApi.ReadManagerLogs(ctx, since)
  2221. }
  2222. return nil, errors.Wrap(httperrors.ErrNotSupported, logType)
  2223. }
  2224. func (b *SBaremetalInstance) clearLogs(ctx context.Context, logType string) error {
  2225. redfishApi := b.GetRedfishCli(ctx)
  2226. if redfishApi == nil {
  2227. return errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
  2228. }
  2229. switch logType {
  2230. case redfish.EVENT_TYPE_SYSTEM:
  2231. return redfishApi.ClearSystemLogs(ctx)
  2232. case redfish.EVENT_TYPE_MANAGER:
  2233. return redfishApi.ClearManagerLogs(ctx)
  2234. }
  2235. return errors.Wrap(httperrors.ErrNotSupported, logType)
  2236. }
  2237. func (b *SBaremetalInstance) doCronJobs(ctx context.Context) {
  2238. for _, job := range b.cronJobs {
  2239. now := time.Now().UTC()
  2240. if job.NeedsToRun(now) {
  2241. // log.Debugf("need to run %s", job.Name())
  2242. func() {
  2243. job.StartRun()
  2244. defer job.StopRun()
  2245. err := job.Do(ctx, now)
  2246. if err != nil {
  2247. log.Errorf("Baremetal %s do cronjob %s fail: %s", b.GetName(), job.Name(), err)
  2248. }
  2249. }()
  2250. }
  2251. }
  2252. }
  2253. func (b *SBaremetalInstance) fetchPowerThermalMetrics(ctx context.Context) ([]influxdb.SKeyValue, []influxdb.SKeyValue, error) {
  2254. redfishApi := b.GetRedfishCli(ctx)
  2255. if redfishApi == nil {
  2256. return nil, nil, errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
  2257. }
  2258. powers, err := redfishApi.GetPower(ctx)
  2259. if err != nil {
  2260. return nil, nil, errors.Wrap(err, "redfishApi.GetPower")
  2261. }
  2262. thermals, err := redfishApi.GetThermal(ctx)
  2263. if err != nil {
  2264. return nil, nil, errors.Wrap(err, "redfishApi.Thermal")
  2265. }
  2266. var powerMetrics []influxdb.SKeyValue
  2267. if len(powers) > 0 {
  2268. powerMetrics = powers[0].ToMetrics()
  2269. }
  2270. thermalMetrics := make([]influxdb.SKeyValue, 0)
  2271. for _, t := range thermals {
  2272. thermalMetrics = append(thermalMetrics, t.ToMetric())
  2273. }
  2274. return powerMetrics, thermalMetrics, nil
  2275. }
  2276. func (b *SBaremetalInstance) GetConsoleJNLP(ctx context.Context) (string, error) {
  2277. cli := b.GetRedfishCli(ctx)
  2278. if cli != nil {
  2279. return cli.GetConsoleJNLP(ctx)
  2280. }
  2281. conf := b.GetIPMIConfig()
  2282. bmc := bmconsole.NewBMCConsole(conf.IpAddr, conf.Username, conf.Password, false)
  2283. manufacture := b.GetManufacture()
  2284. switch strings.ToLower(manufacture) {
  2285. case "hp", "hpe":
  2286. return bmc.GetIloConsoleJNLP(ctx)
  2287. case "dell", "dell inc.":
  2288. return bmc.GetIdracConsoleJNLP(ctx, b.GetSerialNumber(), b.GetModel())
  2289. case "supermicro":
  2290. return bmc.GetSupermicroConsoleJNLP(ctx)
  2291. case "lenovo":
  2292. return bmc.GetLenovoConsoleJNLP(ctx)
  2293. }
  2294. return "", httperrors.NewNotImplementedError("Unsupported manufacture %s", manufacture)
  2295. }
  2296. func (b *SBaremetalInstance) getTags() []influxdb.SKeyValue {
  2297. tags := []influxdb.SKeyValue{
  2298. {
  2299. Key: "id",
  2300. Value: b.GetId(),
  2301. },
  2302. {
  2303. Key: "name",
  2304. Value: b.GetName(),
  2305. },
  2306. {
  2307. Key: "zone_id",
  2308. Value: b.GetZoneId(),
  2309. },
  2310. {
  2311. Key: "zone",
  2312. Value: b.GetZoneName(),
  2313. },
  2314. {
  2315. Key: "boot_mode",
  2316. Value: b.GetBootMode(),
  2317. },
  2318. {
  2319. Key: "host_type",
  2320. Value: "baremetal",
  2321. },
  2322. {
  2323. Key: "region",
  2324. Value: b.GetRegion(),
  2325. },
  2326. {
  2327. Key: "region_id",
  2328. Value: b.GetRegionId(),
  2329. },
  2330. {
  2331. Key: "sn",
  2332. Value: b.GetSerialNumber(),
  2333. },
  2334. {
  2335. Key: "manufacture",
  2336. Value: b.GetManufacture(),
  2337. },
  2338. {
  2339. Key: "model",
  2340. Value: b.GetModel(),
  2341. },
  2342. {
  2343. Key: "status",
  2344. Value: b.GetStatus(),
  2345. },
  2346. {
  2347. Key: "ncpu",
  2348. Value: b.GetNodeCount(),
  2349. },
  2350. {
  2351. Key: "mem_gb",
  2352. Value: b.GetMemGb(),
  2353. },
  2354. }
  2355. srvId := b.GetServerId()
  2356. if len(srvId) > 0 {
  2357. tags = append(tags, influxdb.SKeyValue{
  2358. Key: "server_id",
  2359. Value: srvId,
  2360. })
  2361. tags = append(tags, influxdb.SKeyValue{
  2362. Key: "server",
  2363. Value: b.GetServerName(),
  2364. })
  2365. }
  2366. return tags
  2367. }
  2368. type SBaremetalServer struct {
  2369. baremetal *SBaremetalInstance
  2370. desc *jsonutils.JSONDict
  2371. }
  2372. func newBaremetalServer(baremetal *SBaremetalInstance, desc *jsonutils.JSONDict) (*SBaremetalServer, error) {
  2373. server := &SBaremetalServer{
  2374. baremetal: baremetal,
  2375. desc: desc,
  2376. }
  2377. err := server.SaveDesc(desc)
  2378. return server, err
  2379. }
  2380. func (server *SBaremetalServer) GetId() string {
  2381. id, err := server.desc.GetString("uuid")
  2382. if err != nil {
  2383. log.Fatalf("Get id from desc error: %v", err)
  2384. }
  2385. return id
  2386. }
  2387. func (server *SBaremetalServer) GetName() string {
  2388. id, err := server.desc.GetString("name")
  2389. if err != nil {
  2390. log.Errorf("Get name from desc %s error: %v", server.desc.String(), err)
  2391. }
  2392. return id
  2393. }
  2394. func (server *SBaremetalServer) GetHostName() string {
  2395. hostname, err := server.desc.GetString("hostname")
  2396. if err != nil {
  2397. log.Errorf("Get hostname from desc %s error: %v", server.desc.String(), err)
  2398. }
  2399. return hostname
  2400. }
  2401. func (server *SBaremetalServer) GetOsName() string {
  2402. osName, err := server.desc.GetString("os_name")
  2403. if err != nil {
  2404. log.Errorf("Get os name from desc %s error: %v", server.desc.String(), err)
  2405. }
  2406. return osName
  2407. }
  2408. func (server *SBaremetalServer) SaveDesc(desc jsonutils.JSONObject) error {
  2409. if desc != nil {
  2410. server.desc = desc.(*jsonutils.JSONDict)
  2411. }
  2412. return ioutil.WriteFile(server.baremetal.GetServerDescFilePath(), []byte(server.desc.String()), 0644)
  2413. }
  2414. func (s *SBaremetalServer) RemoveDesc() {
  2415. os.Remove(s.baremetal.GetServerDescFilePath())
  2416. s.desc = nil
  2417. s.baremetal = nil
  2418. }
  2419. func (s *SBaremetalServer) GetRootTemplateId() string {
  2420. rootDisk, err := s.desc.GetAt(0, "disks")
  2421. if err != nil {
  2422. log.Errorf("Can't found root disk")
  2423. return ""
  2424. }
  2425. id, _ := rootDisk.GetString("template_id")
  2426. return id
  2427. }
  2428. func (s *SBaremetalServer) GetMetadata() (*jsonutils.JSONDict, error) {
  2429. metadata, err := s.desc.Get("metadata")
  2430. if err != nil {
  2431. return nil, errors.Wrap(err, "get desc.metadata")
  2432. }
  2433. return metadata.(*jsonutils.JSONDict), nil
  2434. }
  2435. func (s *SBaremetalServer) GetRootDiskMatcher() (*api.BaremetalRootDiskMatcher, error) {
  2436. matcher, err := s.getRootDiskMatcher()
  2437. if err != nil && errors.Cause(err) != errors.ErrNotFound {
  2438. return nil, errors.Wrap(err, "getRootDiskMatcher")
  2439. }
  2440. if matcher == nil {
  2441. matcher = &api.BaremetalRootDiskMatcher{}
  2442. }
  2443. rootDiskObj, err := s.GetRootDiskObj()
  2444. if err != nil {
  2445. return nil, errors.Wrap(err, "GetRootDiskObj")
  2446. }
  2447. pciPath, _ := rootDiskObj.GetString("pci_path")
  2448. if pciPath != "" {
  2449. matcher.PCIPath = pciPath
  2450. }
  2451. return matcher, nil
  2452. }
  2453. func (s *SBaremetalServer) getRootDiskMatcher() (*api.BaremetalRootDiskMatcher, error) {
  2454. metadata, err := s.GetMetadata()
  2455. if err != nil {
  2456. return nil, errors.Wrap(err, "get metadata")
  2457. }
  2458. if !metadata.Contains(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER) {
  2459. return nil, errors.Wrapf(errors.ErrNotFound, "not found %s in metadata", api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
  2460. }
  2461. jStr, _ := metadata.GetString(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
  2462. jObj, err := jsonutils.ParseString(jStr)
  2463. if err != nil {
  2464. return nil, errors.Wrapf(err, "parse json string: %s", jStr)
  2465. }
  2466. matcher := new(api.BaremetalRootDiskMatcher)
  2467. if err := jObj.Unmarshal(matcher); err != nil {
  2468. return nil, errors.Wrapf(err, "unmarshal to matcher")
  2469. }
  2470. return matcher, nil
  2471. }
  2472. func (s *SBaremetalServer) GetDiskConfig() ([]*api.BaremetalDiskConfig, error) {
  2473. layouts := make([]baremetal.Layout, 0)
  2474. err := s.desc.Unmarshal(&layouts, "disk_config")
  2475. if err != nil {
  2476. return nil, err
  2477. }
  2478. if len(layouts) != 0 {
  2479. firstDisk := layouts[0]
  2480. // convert to normal order if first disk is Linux or PCIE driver
  2481. driver := firstDisk.Conf.Driver
  2482. if utils.IsInStringArray(driver, []string{baremetal.DISK_DRIVER_LINUX, baremetal.DISK_DRIVER_PCIE}) {
  2483. return baremetal.GetLayoutDiskConfig(layouts), nil
  2484. }
  2485. }
  2486. return baremetal.GetLayoutRaidConfig(layouts), nil
  2487. }
  2488. func (s *SBaremetalServer) GetRootDiskObj() (*jsonutils.JSONDict, error) {
  2489. disks, _ := s.desc.GetArray("disks")
  2490. if len(disks) == 0 {
  2491. return nil, errors.Error("Empty disks in desc")
  2492. }
  2493. return disks[0].(*jsonutils.JSONDict), nil
  2494. }
  2495. func (s *SBaremetalServer) NewConfigedSSHPartitionTool(term *ssh.Client) (*disktool.SSHPartitionTool, error) {
  2496. raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
  2497. if err != nil {
  2498. return nil, errors.Wrap(err, "DetectStorageInfo")
  2499. }
  2500. storages := make([]*baremetal.BaremetalStorage, 0)
  2501. storages = append(storages, raid...)
  2502. storages = append(storages, nonRaid...)
  2503. storages = append(storages, pcie...)
  2504. confs, err := s.GetDiskConfig()
  2505. if err != nil {
  2506. return nil, err
  2507. }
  2508. layouts, err := baremetal.CalculateLayout(confs, storages)
  2509. if err != nil {
  2510. return nil, fmt.Errorf("CalculateLayout: %v", err)
  2511. }
  2512. log.Errorf("NewConfigedSSHPartitionTool layouts: %s", jsonutils.Marshal(layouts))
  2513. diskConfs := baremetal.GroupLayoutResultsByDriverAdapter(layouts)
  2514. for _, dConf := range diskConfs {
  2515. driver := dConf.Driver
  2516. adapter := dConf.Adapter
  2517. isSoftRaid := baremetal.DISK_DRIVERS_SOFT_RAID.Has(driver)
  2518. raidDrv := raiddrivers.GetDriver(driver, term)
  2519. if raidDrv != nil {
  2520. if err := raidDrv.ParsePhyDevs(); err != nil {
  2521. return nil, fmt.Errorf("RaidDriver %s parse physical devices: %v", raidDrv.GetName(), err)
  2522. }
  2523. if isSoftRaid {
  2524. devs := make([]*baremetal.BaremetalStorage, 0)
  2525. for _, layout := range layouts {
  2526. if len(layout.Disks) > 0 && layout.Disks[0].Driver == driver && layout.Disks[0].Adapter == dConf.Adapter {
  2527. devs = append(devs, layout.Disks...)
  2528. }
  2529. }
  2530. log.Infof("SetDevicesForAdapter %v", jsonutils.Marshal(devs))
  2531. if mdadmDrver, ok := raidDrv.(raid2.IRaidDeviceSetter); ok {
  2532. mdadmDrver.SetDevicesForAdapter(dConf.Adapter, devs)
  2533. }
  2534. }
  2535. if err := raiddrivers.PostBuildRaid(raidDrv, adapter); err != nil {
  2536. return nil, fmt.Errorf("Build %s raid failed: %v", raidDrv.GetName(), err)
  2537. }
  2538. time.Sleep(10 * time.Second) // wait 10 seconds for raid status OK
  2539. }
  2540. }
  2541. matcher, err := s.GetRootDiskMatcher()
  2542. if errors.Cause(err) != errors.ErrNotFound {
  2543. log.Errorf("GetRootDiskMatcher: %v", err)
  2544. }
  2545. tool, err := disktool.NewSSHPartitionTool(term, layouts, matcher)
  2546. if err != nil {
  2547. return nil, errors.Wrap(err, "NewSSHPartitionTool")
  2548. }
  2549. return tool, nil
  2550. }
  2551. func (s *SBaremetalServer) DoDiskConfig(term *ssh.Client) (*disktool.SSHPartitionTool, error) {
  2552. raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, true)
  2553. if err != nil {
  2554. return nil, errors.Wrap(err, "DetectStorageInfo")
  2555. }
  2556. storages := make([]*baremetal.BaremetalStorage, 0)
  2557. storages = append(storages, raid...)
  2558. storages = append(storages, nonRaid...)
  2559. storages = append(storages, pcie...)
  2560. confs, err := s.GetDiskConfig()
  2561. if err != nil {
  2562. return nil, err
  2563. }
  2564. layouts, err := baremetal.CalculateLayout(confs, storages)
  2565. if err != nil {
  2566. return nil, fmt.Errorf("CalculateLayout: %v", err)
  2567. }
  2568. diskConfs := baremetal.GroupLayoutResultsByDriverAdapter(layouts)
  2569. log.Errorf("%s layouts: %s, diskConfs: %s", s.GetName(), jsonutils.Marshal(layouts).PrettyString(), jsonutils.Marshal(diskConfs).PrettyString())
  2570. for _, dConf := range diskConfs {
  2571. driver := dConf.Driver
  2572. raidDrv := raiddrivers.GetDriver(driver, term)
  2573. if raidDrv != nil {
  2574. if err := raidDrv.ParsePhyDevs(); err != nil {
  2575. return nil, fmt.Errorf("RaidDriver %s parse physical devices: %v", raidDrv.GetName(), err)
  2576. }
  2577. raidDrv.CleanRaid()
  2578. }
  2579. }
  2580. for _, dConf := range diskConfs {
  2581. driver := dConf.Driver
  2582. adapter := dConf.Adapter
  2583. isSoftRaid := baremetal.DISK_DRIVERS_SOFT_RAID.Has(driver)
  2584. raidDrv := raiddrivers.GetDriver(driver, term)
  2585. if raidDrv != nil {
  2586. if err := raidDrv.ParsePhyDevs(); err != nil {
  2587. return nil, fmt.Errorf("RaidDriver %s parse physical devices: %v", raidDrv.GetName(), err)
  2588. }
  2589. if isSoftRaid {
  2590. devs := make([]*baremetal.BaremetalStorage, 0)
  2591. for _, layout := range layouts {
  2592. if len(layout.Disks) > 0 && layout.Disks[0].Driver == driver && layout.Disks[0].Adapter == dConf.Adapter {
  2593. devs = append(devs, layout.Disks...)
  2594. }
  2595. }
  2596. log.Infof("SetDevicesForAdapter %v", jsonutils.Marshal(devs))
  2597. if mdadmDriver, ok := raidDrv.(raid2.IRaidDeviceSetter); ok {
  2598. mdadmDriver.SetDevicesForAdapter(dConf.Adapter, devs)
  2599. }
  2600. }
  2601. if err := raiddrivers.BuildRaid(raidDrv, dConf.Configs, adapter); err != nil {
  2602. return nil, fmt.Errorf("Build %s raid failed: %v", raidDrv.GetName(), err)
  2603. }
  2604. time.Sleep(10 * time.Second) // wait 10 seconds for raid status OK
  2605. }
  2606. }
  2607. matcher, err := s.GetRootDiskMatcher()
  2608. if errors.Cause(err) != errors.ErrNotFound {
  2609. log.Errorf("GetRootDiskMatcher: %v", err)
  2610. }
  2611. tool, err := disktool.NewSSHPartitionTool(term, layouts, matcher)
  2612. if err != nil {
  2613. return nil, errors.Wrap(err, "NewSSHPartitionTool")
  2614. }
  2615. maxTries := 60
  2616. for tried := 0; !tool.IsAllDisksReady() && tried < maxTries; tried++ {
  2617. time.Sleep(5 * time.Second)
  2618. tool.RetrieveDiskInfo(matcher)
  2619. log.Warningf("disktool not ready string: %s", tool.DebugString())
  2620. }
  2621. if !tool.IsAllDisksReady() {
  2622. return nil, fmt.Errorf("Raid disks are not ready???")
  2623. }
  2624. return tool, nil
  2625. }
  2626. func (s *SBaremetalServer) DoDiskUnconfig(term *ssh.Client) error {
  2627. // tear down raid
  2628. driver := s.baremetal.GetStorageDriver()
  2629. raidDrv := raiddrivers.GetDriver(driver, term)
  2630. if raidDrv != nil {
  2631. if err := raidDrv.ParsePhyDevs(); err != nil {
  2632. return err
  2633. }
  2634. raidDrv.CleanRaid()
  2635. }
  2636. return nil
  2637. }
  2638. func (s *SBaremetalServer) DoEraseDisk(term *ssh.Client) error {
  2639. // soft raid should stop mdadm first
  2640. if err := mdadm.CleanRaid(term); err != nil {
  2641. return err
  2642. }
  2643. cmd := "/lib/mos/partdestroy.sh"
  2644. _, err := term.Run(cmd)
  2645. return err
  2646. }
  2647. func replaceHostAddr(urlStr string, addr string) string {
  2648. urlComp, _ := url.Parse(urlStr)
  2649. commaPos := strings.IndexByte(urlComp.Host, ':')
  2650. if commaPos >= 0 {
  2651. urlComp.Host = addr + urlComp.Host[commaPos:]
  2652. } else {
  2653. urlComp.Host = addr
  2654. }
  2655. return urlComp.String()
  2656. }
  2657. func (s *SBaremetalServer) doCreateRoot(term *ssh.Client, devName string, disableImageCache bool) error {
  2658. session := s.baremetal.GetClientSession()
  2659. token := session.GetToken().GetTokenString()
  2660. urlStr := s.baremetal.GetImageUrl(disableImageCache)
  2661. imageId := s.GetRootTemplateId()
  2662. cmd := fmt.Sprintf("/lib/mos/rootcreate.sh %s %s %s %s", token, urlStr, imageId, devName)
  2663. log.Infof("rootcreate cmd: %q", cmd)
  2664. if _, err := term.Run(cmd); err != nil {
  2665. return fmt.Errorf("Root create fail: %v", err)
  2666. }
  2667. return nil
  2668. }
  2669. func (s *SBaremetalServer) DoPartitionDisk(tool *disktool.SSHPartitionTool, term *ssh.Client, disableImageCache bool) (*disktool.DiskPartitions, []*disktool.Partition, error) {
  2670. raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
  2671. if err != nil {
  2672. return nil, nil, errors.Wrap(err, "DetectStorageInfo")
  2673. }
  2674. storages := make([]*baremetal.BaremetalStorage, 0)
  2675. storages = append(storages, raid...)
  2676. storages = append(storages, nonRaid...)
  2677. storages = append(storages, pcie...)
  2678. // confs, err := s.GetDiskConfig()
  2679. // if err != nil {
  2680. // return nil, errors.Wrapf(err, "do disk config")
  2681. // }
  2682. // layouts, err := baremetal.CalculateLayout(confs, storages)
  2683. // if err != nil {
  2684. // return nil, errors.Wrapf(err, "CalculateLayout")
  2685. // }
  2686. //
  2687. // tool, err := disktool.NewSSHPartitionTool(term, layouts)
  2688. // if err != nil {
  2689. // return nil, errors.Wrapf(err, "NewSSHPartitionTool")
  2690. // }
  2691. disks, _ := s.desc.GetArray("disks")
  2692. if len(disks) == 0 {
  2693. return nil, nil, errors.Error("Empty disks in desc")
  2694. }
  2695. rootImageId := s.GetRootTemplateId()
  2696. diskOffset := 0
  2697. rootDisk := tool.GetRootDisk()
  2698. log.Infof("root disk name %s", rootDisk.GetDevName())
  2699. if len(rootImageId) > 0 {
  2700. rootDiskObj := disks[0]
  2701. rootSize, _ := rootDiskObj.Int("size")
  2702. err = s.doCreateRoot(term, rootDisk.GetDevName(), disableImageCache)
  2703. if err != nil {
  2704. return rootDisk, nil, errors.Wrap(err, "Failed to create root")
  2705. }
  2706. tool.RetrievePartitionInfo()
  2707. parts := tool.GetPartitions()
  2708. if len(parts) == 0 {
  2709. return rootDisk, nil, errors.Error("Root disk create failed, no partitions")
  2710. }
  2711. log.Infof("Resize root to %d MB", rootSize)
  2712. if err := tool.ResizePartition(0, rootSize); err != nil {
  2713. return rootDisk, nil, errors.Wrapf(err, "Fail to resize root to %d", rootSize)
  2714. }
  2715. diskOffset = 1
  2716. } else {
  2717. tool.RetrievePartitionInfo()
  2718. parts := tool.GetPartitions()
  2719. if len(parts) > 0 {
  2720. return rootDisk, nil, errors.Error("should no partition!!!")
  2721. }
  2722. }
  2723. if len(disks) > diskOffset {
  2724. for _, disk := range disks[diskOffset:] {
  2725. sz, err := disk.Int("size")
  2726. if err != nil {
  2727. sz = -1
  2728. }
  2729. fs, _ := disk.GetString("fs")
  2730. uuid, _ := disk.GetString("disk_id")
  2731. driver, _ := disk.GetString("driver")
  2732. log.Infof("Create partition %d %s", sz, fs)
  2733. if err := tool.CreatePartition(-1, sz, fs, true, driver, uuid); err != nil {
  2734. return rootDisk, nil, errors.Wrapf(err, "Fail to create disk %s", disk.String())
  2735. }
  2736. }
  2737. }
  2738. log.Infof("Finish create partitions")
  2739. return rootDisk, tool.GetPartitions(), nil
  2740. }
  2741. func (s *SBaremetalServer) DoRebuildRootDisk(tool *disktool.SSHPartitionTool, term *ssh.Client, disableImageCache bool) (*disktool.DiskPartitions, []*disktool.Partition, error) {
  2742. // raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
  2743. // if err != nil {
  2744. // return nil, err
  2745. // }
  2746. // storages := make([]*baremetal.BaremetalStorage, 0)
  2747. // storages = append(storages, raid...)
  2748. // storages = append(storages, nonRaid...)
  2749. // storages = append(storages, pcie...)
  2750. // confs, err := s.GetDiskConfig()
  2751. // if err != nil {
  2752. // return nil, err
  2753. // }
  2754. // layouts, err := baremetal.CalculateLayout(confs, storages)
  2755. // if err != nil {
  2756. // return nil, err
  2757. // }
  2758. //
  2759. // tool, err := disktool.NewSSHPartitionTool(term, layouts)
  2760. // if err != nil {
  2761. // return nil, err
  2762. // }
  2763. disks, _ := s.desc.GetArray("disks")
  2764. if len(disks) == 0 {
  2765. return nil, nil, fmt.Errorf("Empty disks in desc")
  2766. }
  2767. rootDisk := disks[0]
  2768. rootSize, _ := rootDisk.Int("size")
  2769. rd := tool.GetRootDisk()
  2770. err := s.doCreateRoot(term, rd.GetDevName(), disableImageCache)
  2771. if err != nil {
  2772. return rd, nil, fmt.Errorf("Failed to create root: %v", err)
  2773. }
  2774. tool.RetrievePartitionInfo()
  2775. if err := rd.ReInitInfo(); err != nil {
  2776. return rd, nil, errors.Wrap(err, "Reinit root disk after create root")
  2777. }
  2778. log.Infof("Resize root to %d MB", rootSize)
  2779. if err := rd.ResizePartition(rootSize); err != nil {
  2780. return rd, nil, fmt.Errorf("Fail to resize root to %d, err: %v", rootSize, err)
  2781. }
  2782. if len(disks) > 1 {
  2783. for _, disk := range disks[1:] {
  2784. sz, err := disk.Int("size")
  2785. if err != nil {
  2786. sz = -1
  2787. }
  2788. fs, _ := disk.GetString("fs")
  2789. uuid, _ := disk.GetString("disk_id")
  2790. log.Infof("Create partition %d %s", sz, fs)
  2791. if err := rd.CreatePartition(sz, fs, false, uuid); err != nil {
  2792. log.Errorf("Rebuild root create (%s, %d, %s) partition error: %v", uuid, sz, fs, err)
  2793. break
  2794. }
  2795. }
  2796. }
  2797. log.Infof("Finish create partitions")
  2798. parts := rd.GetPartitions()
  2799. restDisks := tool.Disks()
  2800. if len(restDisks) > 1 {
  2801. restDisks = restDisks[1:]
  2802. }
  2803. for _, d := range restDisks {
  2804. parts = append(parts, d.GetPartitions()...)
  2805. }
  2806. return rd, parts, nil
  2807. }
  2808. func (s *SBaremetalServer) SyncPartitionSize(term *ssh.Client, rootDisk *disktool.DiskPartitions, parts []*disktool.Partition) ([]jsonutils.JSONObject, error) {
  2809. disks, _ := s.desc.GetArray("disks")
  2810. // calculate root partitions count
  2811. rootPartsCnt := len(parts) - len(disks) + 1
  2812. if len(parts) < len(disks) {
  2813. // HACK: rebuild root disk
  2814. rootPartsCnt = len(parts)
  2815. }
  2816. rootParts := parts[0:rootPartsCnt]
  2817. dataParts := parts[rootPartsCnt:]
  2818. idx := 0
  2819. // set root disk attributes that returns to region service
  2820. size := (rootParts[len(rootParts)-1].GetEnd() + 1) * 512 / 1024 / 1024
  2821. rootDiskObj := disks[idx].(*jsonutils.JSONDict)
  2822. rootDiskObj.Set("size", jsonutils.NewInt(int64(size)))
  2823. rootDiskObj.Set("pci_path", jsonutils.NewString(rootDisk.GetPCIPath()))
  2824. idx += 1
  2825. for _, p := range dataParts {
  2826. sizeMB, err := p.GetSizeMB()
  2827. if err != nil {
  2828. return nil, errors.Wrap(err, "GetSizeMB")
  2829. }
  2830. disks[idx].(*jsonutils.JSONDict).Set("size", jsonutils.NewInt(int64(sizeMB)))
  2831. disks[idx].(*jsonutils.JSONDict).Set("dev", jsonutils.NewString(p.GetDev()))
  2832. idx++
  2833. }
  2834. s.desc.Set("disks", jsonutils.NewArray(disks...))
  2835. return disks, nil
  2836. }
  2837. func (s *SBaremetalServer) DoDeploy(tool *disktool.SSHPartitionTool, term *ssh.Client, data jsonutils.JSONObject, isInit bool) (jsonutils.JSONObject, error) {
  2838. publicKey := deployapi.GetKeys(data)
  2839. isRandomPassword := false
  2840. password, _ := data.GetString("password")
  2841. resetPassword := jsonutils.QueryBoolean(data, "reset_password", false)
  2842. if resetPassword && len(password) == 0 {
  2843. password = seclib.RandomPassword(12)
  2844. isRandomPassword = true
  2845. }
  2846. deployArray := make([]*deployapi.DeployContent, 0)
  2847. if data.Contains("deploys") {
  2848. err := data.Unmarshal(&deployArray, "deploys")
  2849. if err != nil {
  2850. return nil, errors.Wrapf(err, "unmarshal to array of deployapi.DeployContent")
  2851. }
  2852. }
  2853. userData, _ := s.desc.GetString("user_data")
  2854. deployInfo := deployapi.NewDeployInfo(publicKey, deployArray,
  2855. password, isRandomPassword, isInit, true, o.Options.LinuxDefaultRootUser, o.Options.WindowsDefaultAdminUser, false, "",
  2856. false, "",
  2857. userData,
  2858. )
  2859. return s.deployFs(tool, term, deployInfo)
  2860. }
  2861. func (s *SBaremetalServer) deployFs(tool *disktool.SSHPartitionTool, term *ssh.Client, deployInfo *deployapi.DeployInfo) (jsonutils.JSONObject, error) {
  2862. raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
  2863. if err != nil {
  2864. return nil, err
  2865. }
  2866. storages := make([]*baremetal.BaremetalStorage, 0)
  2867. storages = append(storages, raid...)
  2868. storages = append(storages, nonRaid...)
  2869. storages = append(storages, pcie...)
  2870. confs, err := s.GetDiskConfig()
  2871. if err != nil {
  2872. return nil, err
  2873. }
  2874. layouts, err := baremetal.CalculateLayout(confs, storages)
  2875. if err != nil {
  2876. return nil, err
  2877. }
  2878. rootDev, rootfs, err := sshpart.MountSSHRootfs(tool, term, layouts)
  2879. if err != nil {
  2880. return nil, fmt.Errorf("Find rootfs error: %s", err)
  2881. }
  2882. defer func() {
  2883. rootDev.Umount()
  2884. }()
  2885. if strings.ToLower(rootfs.GetOs()) == "windows" {
  2886. return nil, fmt.Errorf("Unsupported OS: %s", rootfs.GetOs())
  2887. }
  2888. deployDesc, err := deployapi.GuestJsonDescToDeployDesc(s.desc)
  2889. if err != nil {
  2890. return nil, errors.Wrap(err, "To deploy desc fail")
  2891. }
  2892. deployDesc.Hypervisor = api.HYPERVISOR_BAREMETAL
  2893. deployDesc, err = s.reIndexDescNics(term, deployDesc)
  2894. if err != nil {
  2895. return nil, errors.Wrap(err, "reIndexDescNics")
  2896. }
  2897. return guestfs.DeployGuestFs(rootfs, deployDesc, deployInfo)
  2898. }
  2899. func (s *SBaremetalServer) reIndexDescNics(term *ssh.Client, desc *deployapi.GuestDesc) (*deployapi.GuestDesc, error) {
  2900. rNics, err := tasks.GetNicsInfo(term)
  2901. if err != nil {
  2902. return nil, errors.Wrapf(err, "Get server %s remote nics", s.GetName())
  2903. }
  2904. findRemoteNic := func(mac string) (int, *types.SNicDevInfo) {
  2905. for idx, nic := range rNics {
  2906. if nic.Mac.String() == mac {
  2907. return idx, rNics[idx]
  2908. }
  2909. }
  2910. return -1, nil
  2911. }
  2912. reIndexNics := func(nics []*deployapi.Nic) ([]*deployapi.Nic, error) {
  2913. for idx, nic := range nics {
  2914. if nic.GetNicType() == string(api.NIC_TYPE_IPMI) || nic.GetIndex() >= 0 {
  2915. continue
  2916. }
  2917. rIdx, rNic := findRemoteNic(nic.GetMac())
  2918. if rNic == nil {
  2919. return nil, errors.Errorf("Not found remote nic by mac %q", nic.GetMac())
  2920. }
  2921. if nics[idx].Index != int32(rIdx) {
  2922. log.Infof("Reindex nic %s index %d to %d", nics[idx].GetMac(), nics[idx].Index, rIdx)
  2923. nics[idx].Index = int32(rIdx)
  2924. }
  2925. }
  2926. return nics, nil
  2927. }
  2928. nics, err := reIndexNics(desc.Nics)
  2929. if err != nil {
  2930. return nil, errors.Wrap(err, "Reindex nics")
  2931. }
  2932. desc.Nics = nics
  2933. sNics, err := reIndexNics(desc.NicsStandby)
  2934. if err != nil {
  2935. return nil, errors.Wrap(err, "Reindex standby nics")
  2936. }
  2937. desc.NicsStandby = sNics
  2938. return desc, nil
  2939. }
  2940. func (s *SBaremetalServer) GetNics() []types.SServerNic {
  2941. nics := []types.SServerNic{}
  2942. err := s.desc.Unmarshal(&nics, "nics")
  2943. if err != nil {
  2944. log.Errorf("Unmarshal desc to get server nics error: %v", err)
  2945. return nil
  2946. }
  2947. return nics
  2948. }
  2949. func (s *SBaremetalServer) GetNicByMac(mac net.HardwareAddr) *types.SNic {
  2950. for _, n := range s.GetNics() {
  2951. if n.GetMac().String() == mac.String() {
  2952. nic := n.ToNic()
  2953. return &nic
  2954. }
  2955. }
  2956. return nil
  2957. }