pod.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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 guestdrivers
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "net/url"
  20. "k8s.io/apimachinery/pkg/util/proxy"
  21. "yunion.io/x/cloudmux/pkg/cloudprovider"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/util/httputils"
  26. "yunion.io/x/pkg/util/rbacscope"
  27. "yunion.io/x/pkg/util/sets"
  28. "yunion.io/x/onecloud/pkg/apis"
  29. api "yunion.io/x/onecloud/pkg/apis/compute"
  30. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  31. "yunion.io/x/onecloud/pkg/apis/image"
  32. "yunion.io/x/onecloud/pkg/appsrv"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  36. "yunion.io/x/onecloud/pkg/compute/models"
  37. "yunion.io/x/onecloud/pkg/compute/options"
  38. "yunion.io/x/onecloud/pkg/compute/utils"
  39. "yunion.io/x/onecloud/pkg/httperrors"
  40. "yunion.io/x/onecloud/pkg/mcclient"
  41. "yunion.io/x/onecloud/pkg/util/pod/remotecommand/spdy"
  42. )
  43. var _ models.IPodDriver = new(SPodDriver)
  44. type SPodDriver struct {
  45. SKVMGuestDriver
  46. }
  47. func init() {
  48. driver := SPodDriver{}
  49. models.RegisterGuestDriver(&driver)
  50. }
  51. func (p *SPodDriver) newUnsupportOperationError(option string) error {
  52. return httperrors.NewUnsupportOperationError("Container not support %s", option)
  53. }
  54. func (p *SPodDriver) GetHypervisor() string {
  55. return api.HYPERVISOR_POD
  56. }
  57. func (p *SPodDriver) GetProvider() string {
  58. return api.CLOUD_PROVIDER_ONECLOUD
  59. }
  60. func (p *SPodDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
  61. for i, d := range input.Disks {
  62. if d.Format != "" {
  63. if d.Format != image.IMAGE_DISK_FORMAT_RAW {
  64. return nil, httperrors.NewInputParameterError("not support format %s for disk %d", d.Format, i)
  65. }
  66. }
  67. }
  68. if input.Pod == nil {
  69. return nil, httperrors.NewNotEmptyError("pod data is empty")
  70. }
  71. if len(input.Pod.Containers) == 0 {
  72. return nil, httperrors.NewNotEmptyError("containers data is empty")
  73. }
  74. // validate port mappings
  75. /*if err := p.validatePortMappings(input.Pod); err != nil {
  76. return nil, errors.Wrap(err, "validate port mappings")
  77. }*/
  78. ctrNames := sets.NewString()
  79. volUniqNames := sets.NewString()
  80. for idx, ctr := range input.Pod.Containers {
  81. if err := p.validateContainerData(ctx, userCred, idx, input.Name, ctr, input); err != nil {
  82. return nil, errors.Wrapf(err, "data of %d container", idx)
  83. }
  84. if ctrNames.Has(ctr.Name) {
  85. return nil, httperrors.NewDuplicateNameError("same name %s of containers", ctr.Name)
  86. }
  87. ctrNames.Insert(ctr.Name)
  88. for volIdx := range ctr.VolumeMounts {
  89. vol := ctr.VolumeMounts[volIdx]
  90. if vol.UniqueName != "" {
  91. if volUniqNames.Has(vol.UniqueName) {
  92. return nil, httperrors.NewDuplicateNameError("same volume unique name %s", fmt.Sprintf("container %s volume_mount %d %s", ctr.Name, volIdx, vol.UniqueName))
  93. } else {
  94. volUniqNames.Insert(vol.UniqueName)
  95. }
  96. }
  97. }
  98. }
  99. err := utils.TopologicalSortContainers(
  100. input.Pod.Containers,
  101. func(ctr *api.PodContainerCreateInput) string { return ctr.Name },
  102. func(ctr *api.PodContainerCreateInput) []string { return ctr.DependsOn },
  103. )
  104. if err != nil {
  105. return nil, errors.Wrap(err, "invalid container dependency")
  106. }
  107. return input, nil
  108. }
  109. /*func (p *SPodDriver) validatePortMappings(input *api.PodCreateInput) error {
  110. usedPorts := make(map[api.PodPortMappingProtocol]sets.Int)
  111. for idx, pm := range input.PortMappings {
  112. ports, ok := usedPorts[pm.Protocol]
  113. if !ok {
  114. ports = sets.NewInt()
  115. }
  116. if pm.HostPort != nil {
  117. if ports.Has(*pm.HostPort) {
  118. return httperrors.NewInputParameterError("%s host_port %d is already specified", pm.Protocol, *pm.HostPort)
  119. }
  120. ports.Insert(*pm.HostPort)
  121. }
  122. usedPorts[pm.Protocol] = ports
  123. if err := p.validatePortMapping(pm); err != nil {
  124. return errors.Wrapf(err, "validate portmapping %d", idx)
  125. }
  126. }
  127. return nil
  128. }*/
  129. func (p *SPodDriver) validateHostPortMapping(hostId string, pm *api.PodPortMapping) error {
  130. // TODO:
  131. return nil
  132. }
  133. func (p *SPodDriver) validateContainerData(ctx context.Context, userCred mcclient.TokenCredential, idx int, defaultNamePrefix string, ctr *api.PodContainerCreateInput, input *api.ServerCreateInput) error {
  134. if ctr.Name == "" {
  135. ctr.Name = fmt.Sprintf("%s-%d", defaultNamePrefix, idx)
  136. }
  137. if err := models.GetContainerManager().ValidateSpec(ctx, userCred, &ctr.ContainerSpec, nil, nil); err != nil {
  138. return errors.Wrap(err, "validate container spec")
  139. }
  140. if err := p.validateContainerVolumeMounts(ctx, userCred, ctr, input); err != nil {
  141. return errors.Wrap(err, "validate container volumes")
  142. }
  143. return nil
  144. }
  145. func (p *SPodDriver) validateContainerVolumeMounts(ctx context.Context, userCred mcclient.TokenCredential, ctr *api.PodContainerCreateInput, input *api.ServerCreateInput) error {
  146. for idx, vm := range ctr.VolumeMounts {
  147. if err := p.validateContainerVolumeMount(ctx, userCred, vm, input); err != nil {
  148. return errors.Wrapf(err, "validate volume mount %d", idx)
  149. }
  150. }
  151. return nil
  152. }
  153. func (p *SPodDriver) validateContainerVolumeMount(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount, input *api.ServerCreateInput) error {
  154. if vm.Type == "" {
  155. return httperrors.NewNotEmptyError("type is required")
  156. }
  157. if vm.MountPath == "" {
  158. return httperrors.NewNotEmptyError("mount_path is required")
  159. }
  160. drv, err := models.GetContainerVolumeMountDriverWithError(vm.Type)
  161. if err != nil {
  162. return errors.Wrapf(err, "get container volume mount driver %s", vm.Type)
  163. }
  164. if err := drv.ValidatePodCreateData(ctx, userCred, vm, input); err != nil {
  165. return errors.Wrapf(err, "validate %s create data", vm.Type)
  166. }
  167. return nil
  168. }
  169. func (p *SPodDriver) validatePortRange(portRange *api.PodPortMappingPortRange) error {
  170. if portRange != nil {
  171. if portRange.Start > portRange.End {
  172. return httperrors.NewInputParameterError("port range start %d is large than %d", portRange.Start, portRange.End)
  173. }
  174. if portRange.Start <= api.POD_PORT_MAPPING_RANGE_START {
  175. return httperrors.NewInputParameterError("port range start %d <= %d", api.POD_PORT_MAPPING_RANGE_START, portRange.Start)
  176. }
  177. if portRange.End > api.POD_PORT_MAPPING_RANGE_END {
  178. return httperrors.NewInputParameterError("port range end %d > %d", api.POD_PORT_MAPPING_RANGE_END, portRange.End)
  179. }
  180. }
  181. return nil
  182. }
  183. func (p *SPodDriver) validatePort(port int, start int, end int) error {
  184. if port < start || port > end {
  185. return httperrors.NewInputParameterError("port number %d isn't within %d to %d", port, start, end)
  186. }
  187. return nil
  188. }
  189. func (p *SPodDriver) validatePortMapping(pm *api.PodPortMapping) error {
  190. if err := p.validatePortRange(pm.HostPortRange); err != nil {
  191. return err
  192. }
  193. if pm.HostPort != nil {
  194. if err := p.validatePort(*pm.HostPort, api.POD_PORT_MAPPING_RANGE_START, api.POD_PORT_MAPPING_RANGE_END); err != nil {
  195. return errors.Wrap(err, "validate host_port")
  196. }
  197. }
  198. if err := p.validatePort(pm.ContainerPort, 1, 65535); err != nil {
  199. return errors.Wrap(err, "validate container_port")
  200. }
  201. if pm.Protocol == "" {
  202. pm.Protocol = api.PodPortMappingProtocolTCP
  203. }
  204. if !sets.NewString(api.PodPortMappingProtocolUDP, api.PodPortMappingProtocolTCP).Has(string(pm.Protocol)) {
  205. return httperrors.NewInputParameterError("unsupported protocol %s", pm.Protocol)
  206. }
  207. return nil
  208. }
  209. func (p *SPodDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
  210. return cloudprovider.SInstanceCapability{
  211. Hypervisor: p.GetHypervisor(),
  212. Provider: p.GetProvider(),
  213. }
  214. }
  215. // for backward compatibility, deprecated driver
  216. func (p *SPodDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
  217. keys := models.SComputeResourceKeys{}
  218. keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
  219. keys.CloudEnv = api.CLOUD_ENV_ON_PREMISE
  220. keys.Provider = api.CLOUD_PROVIDER_ONECLOUD
  221. keys.Brand = api.ONECLOUD_BRAND_ONECLOUD
  222. keys.Hypervisor = api.HYPERVISOR_POD
  223. return keys
  224. }
  225. func (p *SPodDriver) GetDefaultSysDiskBackend() string {
  226. return api.STORAGE_LOCAL
  227. }
  228. func (p *SPodDriver) GetMinimalSysDiskSizeGb() int {
  229. return options.Options.DefaultDiskSizeMB / 1024
  230. }
  231. func (p *SPodDriver) StartGuestCreateTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, pendingUsage quotas.IQuota, parentTaskId string) error {
  232. task, err := taskman.TaskManager.NewTask(ctx, "PodCreateTask", guest, userCred, data, parentTaskId, "", pendingUsage)
  233. if err != nil {
  234. return errors.Wrap(err, "New PodCreateTask")
  235. }
  236. return task.ScheduleRun(nil)
  237. }
  238. func (p *SPodDriver) RequestGuestHotAddIso(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
  239. // do nothing, call next stage
  240. return task.ScheduleRun(nil)
  241. }
  242. func (p *SPodDriver) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, data *jsonutils.JSONDict, parentTaskId string) error {
  243. guest.SetStatus(ctx, userCred, api.VM_START_START, "")
  244. task, err := taskman.TaskManager.NewTask(ctx, "PodStartTask", guest, userCred, nil, parentTaskId, "", nil)
  245. if err != nil {
  246. return errors.Wrap(err, "New PodStartTask")
  247. }
  248. return task.ScheduleRun(nil)
  249. }
  250. func (p *SPodDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
  251. header := p.getTaskRequestHeader(task)
  252. config := jsonutils.NewDict()
  253. drv, err := guest.GetDriver()
  254. if err != nil {
  255. return err
  256. }
  257. desc, err := drv.GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil)
  258. if err != nil {
  259. return errors.Wrapf(err, "GetJsonDescAtHost")
  260. }
  261. config.Add(desc, "desc")
  262. params := task.GetParams()
  263. if params.Length() > 0 {
  264. config.Add(params, "params")
  265. }
  266. url := fmt.Sprintf("%s/servers/%s/start", host.ManagerUri, guest.Id)
  267. _, body, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, config, false)
  268. if err != nil {
  269. return err
  270. }
  271. resp := new(api.PodStartResponse)
  272. body.Unmarshal(resp)
  273. if resp.IsRunning {
  274. taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
  275. return body, nil
  276. })
  277. }
  278. return nil
  279. }
  280. func (p *SPodDriver) RqeuestSuspendOnHost(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  281. return p.newUnsupportOperationError("suspend")
  282. }
  283. func (p *SPodDriver) RequestSoftReset(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  284. return p.newUnsupportOperationError("soft reset")
  285. }
  286. func (p *SPodDriver) GetGuestVncInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
  287. return nil, p.newUnsupportOperationError("VNC")
  288. }
  289. func (p *SPodDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error {
  290. //guest.SaveDeployInfo(ctx, task.GetUserCred(), data)
  291. // do nothing here
  292. return nil
  293. }
  294. func (p *SPodDriver) CanStop(guest *models.SGuest) error {
  295. if guest.PowerStates == api.VM_POWER_STATES_ON {
  296. return nil
  297. }
  298. return p.SKVMGuestDriver.CanStop(guest)
  299. }
  300. func (p *SPodDriver) StartGuestStopTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  301. task, err := taskman.TaskManager.NewTask(ctx, "PodStopTask", guest, userCred, params, parentTaskId, "", nil)
  302. if err != nil {
  303. return errors.Wrap(err, "New PodStopTask")
  304. }
  305. return task.ScheduleRun(nil)
  306. }
  307. func (p *SPodDriver) StartGuestRestartTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, isForce bool, parentTaskId string) error {
  308. data := jsonutils.NewDict()
  309. data.Set("is_force", jsonutils.NewBool(isForce))
  310. if err := guest.SetStatus(ctx, userCred, api.VM_STOPPING, ""); err != nil {
  311. return err
  312. }
  313. task, err := taskman.TaskManager.NewTask(ctx, "PodRestartTask", guest, userCred, nil, parentTaskId, "", nil)
  314. if err != nil {
  315. return err
  316. }
  317. task.ScheduleRun(nil)
  318. return nil
  319. }
  320. func (p *SPodDriver) StartDeleteGuestTask(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict, parentTaskId string) error {
  321. task, err := taskman.TaskManager.NewTask(ctx, "PodDeleteTask", guest, userCred, params, parentTaskId, "", nil)
  322. if err != nil {
  323. return errors.Wrap(err, "New PodDeleteTask")
  324. }
  325. return task.ScheduleRun(nil)
  326. }
  327. func (p *SPodDriver) StartGuestSyncstatusTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  328. task, err := taskman.TaskManager.NewTask(ctx, "PodSyncstatusTask", guest, userCred, nil, parentTaskId, "", nil)
  329. if err != nil {
  330. return errors.Wrap(err, "New PodSyncstatusTask")
  331. }
  332. return task.ScheduleRun(nil)
  333. }
  334. func (p *SPodDriver) RequestUndeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
  335. url := fmt.Sprintf("%s/servers/%s", host.ManagerUri, guest.Id)
  336. header := p.getTaskRequestHeader(task)
  337. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "DELETE", url, header, nil, false)
  338. return err
  339. }
  340. func (p *SPodDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
  341. desc := guest.GetJsonDescAtHypervisor(ctx, host)
  342. ctrs, err := models.GetContainerManager().GetContainersByPod(guest.GetId())
  343. if err != nil {
  344. return nil, errors.Wrap(err, "GetContainersByPod")
  345. }
  346. ctrDescs := make([]*hostapi.ContainerDesc, len(ctrs))
  347. for idx, ctr := range ctrs {
  348. desc, err := ctr.GetJsonDescAtHost(ctx, userCred)
  349. if err != nil {
  350. return nil, errors.Wrapf(err, "GetJsonDescAtHost of container %s", ctr.GetId())
  351. }
  352. ctrDescs[idx] = desc
  353. }
  354. desc.Containers = ctrDescs
  355. return jsonutils.Marshal(desc), nil
  356. }
  357. func (p *SPodDriver) createContainersOnPod(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
  358. input, err := guest.GetCreateParams(ctx, userCred)
  359. if err != nil {
  360. return errors.Wrap(err, "GetCreateParams")
  361. }
  362. ctrs := make([]*models.SContainer, len(input.Pod.Containers))
  363. for idx, ctr := range input.Pod.Containers {
  364. if obj, err := models.GetContainerManager().CreateOnPod(ctx, userCred, guest.GetOwnerId(), guest, ctr); err != nil {
  365. return errors.Wrapf(err, "create container on pod: %s", guest.GetName())
  366. } else {
  367. ctrs[idx] = obj
  368. }
  369. }
  370. return nil
  371. }
  372. func (p *SPodDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
  373. deployAction, err := task.GetParams().GetString("deploy_action")
  374. if err != nil {
  375. return errors.Wrapf(err, "get deploy_action from task params: %s", task.GetParams())
  376. }
  377. if deployAction == "create" {
  378. if err := p.createContainersOnPod(ctx, task.GetUserCred(), guest); err != nil {
  379. return errors.Wrap(err, "create containers on pod")
  380. }
  381. }
  382. config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
  383. if err != nil {
  384. log.Errorf("GetDeployConfigOnHost error: %v", err)
  385. return err
  386. }
  387. action, err := config.GetString("action")
  388. if err != nil {
  389. return err
  390. }
  391. url := fmt.Sprintf("%s/servers/%s/%s", host.ManagerUri, guest.Id, action)
  392. header := p.getTaskRequestHeader(task)
  393. _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, config, false)
  394. return err
  395. }
  396. func (p *SPodDriver) performContainerAction(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask, action string, data jsonutils.JSONObject) error {
  397. pod := task.GetPod()
  398. ctr := task.GetContainer()
  399. host, _ := pod.GetHost()
  400. url := fmt.Sprintf("%s/pods/%s/containers/%s/%s", host.ManagerUri, pod.GetId(), ctr.GetId(), action)
  401. header := p.getTaskRequestHeader(task)
  402. _, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, data, false)
  403. return err
  404. }
  405. func (p *SPodDriver) getContainerCreateInput(ctx context.Context, userCred mcclient.TokenCredential, ctr *models.SContainer) (*hostapi.ContainerCreateInput, error) {
  406. spec, err := ctr.ToHostContainerSpec(ctx, userCred)
  407. if err != nil {
  408. return nil, errors.Wrap(err, "ToHostContainerSpec")
  409. }
  410. input := &hostapi.ContainerCreateInput{
  411. Name: ctr.GetName(),
  412. GuestId: ctr.GuestId,
  413. Spec: spec,
  414. RestartCount: ctr.RestartCount,
  415. }
  416. return input, nil
  417. }
  418. func (p *SPodDriver) RequestCreateContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  419. ctr := task.GetContainer()
  420. input, err := p.getContainerCreateInput(ctx, userCred, ctr)
  421. if err != nil {
  422. return errors.Wrap(err, "getContainerCreateInput")
  423. }
  424. return p.performContainerAction(ctx, userCred, task, "create", jsonutils.Marshal(input))
  425. }
  426. func (p *SPodDriver) RequestStartContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  427. ctr := task.GetContainer()
  428. input, err := p.getContainerCreateInput(ctx, userCred, ctr)
  429. if err != nil {
  430. return errors.Wrap(err, "getContainerCreateInput")
  431. }
  432. return p.performContainerAction(ctx, userCred, task, "start", jsonutils.Marshal(input))
  433. }
  434. func (p *SPodDriver) RequestStopContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  435. ctr := task.GetContainer()
  436. params := task.GetParams()
  437. params.Add(jsonutils.NewString(ctr.GetName()), "container_name")
  438. params.Add(jsonutils.NewInt(int64(ctr.Spec.ShmSizeMB)), "shm_size_mb")
  439. return p.performContainerAction(ctx, userCred, task, "stop", task.GetParams())
  440. }
  441. func (p *SPodDriver) RequestDeleteContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  442. return p.performContainerAction(ctx, userCred, task, "delete", nil)
  443. }
  444. func (p *SPodDriver) RequestSyncContainerStatus(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  445. return p.performContainerAction(ctx, userCred, task, "sync-status", nil)
  446. }
  447. func (p *SPodDriver) RequestPullContainerImage(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  448. return p.performContainerAction(ctx, userCred, task, "pull-image", task.GetParams())
  449. }
  450. func (p *SPodDriver) RequestAddVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  451. return p.performContainerAction(ctx, userCred, task, "add-volume-mount-post-overlay", task.GetParams())
  452. }
  453. func (p *SPodDriver) RequestRemoveVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  454. return p.performContainerAction(ctx, userCred, task, "remove-volume-mount-post-overlay", task.GetParams())
  455. }
  456. type responder struct {
  457. errorMessage string
  458. }
  459. func (r *responder) Error(w http.ResponseWriter, req *http.Request, err error) {
  460. http.Error(w, err.Error(), http.StatusInternalServerError)
  461. }
  462. func (p *SPodDriver) RequestExecContainer(ctx context.Context, userCred mcclient.TokenCredential, ctr *models.SContainer, input *api.ContainerExecInput) error {
  463. pod := ctr.GetPod()
  464. host, _ := pod.GetHost()
  465. urlPath := fmt.Sprintf("%s/pods/%s/containers/%s/%s?%s", host.ManagerUri, pod.GetId(), ctr.GetId(), "exec", jsonutils.Marshal(input).QueryString())
  466. loc, _ := url.Parse(urlPath)
  467. tokenHeader := mcclient.GetTokenHeaders(userCred)
  468. trans, _, _ := spdy.RoundTripperFor()
  469. handler := proxy.NewUpgradeAwareHandler(loc, trans, false, true, new(responder))
  470. appParams := appsrv.AppContextGetParams(ctx)
  471. newHeader := appParams.Request.Header
  472. for key, vals := range tokenHeader {
  473. for _, val := range vals {
  474. newHeader.Add(key, val)
  475. }
  476. }
  477. appParams.Request.Header = newHeader
  478. appParams.Request.Method = "POST"
  479. handler.ServeHTTP(appParams.Response, appParams.Request)
  480. return nil
  481. }
  482. func (p *SPodDriver) requestContainerSyncAction(ctx context.Context, userCred mcclient.TokenCredential, container *models.SContainer, action string, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  483. pod := container.GetPod()
  484. host, _ := pod.GetHost()
  485. url := fmt.Sprintf("%s/pods/%s/containers/%s/%s", host.ManagerUri, pod.GetId(), container.GetId(), action)
  486. header := mcclient.GetTokenHeaders(userCred)
  487. _, ret, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, input, false)
  488. return ret, err
  489. }
  490. func (p *SPodDriver) RequestExecSyncContainer(ctx context.Context, userCred mcclient.TokenCredential, container *models.SContainer, input *api.ContainerExecSyncInput) (jsonutils.JSONObject, error) {
  491. return p.requestContainerSyncAction(ctx, userCred, container, "exec-sync", jsonutils.Marshal(input))
  492. }
  493. func (p *SPodDriver) RequestSetContainerResourcesLimit(ctx context.Context, userCred mcclient.TokenCredential, container *models.SContainer, limit *apis.ContainerResources) (jsonutils.JSONObject, error) {
  494. return p.requestContainerSyncAction(ctx, userCred, container, "set-resources-limit", jsonutils.Marshal(limit))
  495. }
  496. func (p *SPodDriver) OnDeleteGuestFinalCleanup(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential) error {
  497. // clean disk records in DB
  498. return guest.DeleteAllDisksInDB(ctx, userCred)
  499. }
  500. func (p *SPodDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
  501. // do nothing, call next stage
  502. return p.newUnsupportOperationError("rebuild root")
  503. }
  504. func (p *SPodDriver) GetRandomNetworkTypes() []api.TNetworkType {
  505. return []api.TNetworkType{api.NETWORK_TYPE_CONTAINER, api.NETWORK_TYPE_GUEST, api.NETWORK_TYPE_HOSTLOCAL}
  506. }
  507. func (p *SPodDriver) IsSupportGuestClone() bool {
  508. return false
  509. }
  510. func (p *SPodDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) {
  511. return false, nil
  512. }
  513. func (p *SPodDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
  514. return false, nil
  515. }
  516. func (p *SPodDriver) GetChangeInstanceTypeStatus() ([]string, error) {
  517. return []string{api.VM_READY}, nil
  518. }
  519. func (p *SPodDriver) RequestSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  520. return p.performContainerAction(ctx, userCred, task, "save-volume-mount-to-image", task.GetParams())
  521. }
  522. func (p *SPodDriver) RequestCommitContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
  523. return p.performContainerAction(ctx, userCred, task, "commit", task.GetParams())
  524. }
  525. func (p *SPodDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
  526. /*if guest.GetStatus() != api.VM_READY {
  527. return httperrors.NewNotAcceptableError("pod status %s is not ready", guest.GetStatus())
  528. }*/
  529. return p.SKVMGuestDriver.RequestDiskSnapshot(ctx, guest, task, snapshotId, diskId)
  530. }
  531. func (p *SPodDriver) RequestDeleteSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
  532. /*if guest.GetStatus() != api.VM_READY {
  533. return httperrors.NewNotAcceptableError("pod status %s is not ready", guest.GetStatus())
  534. }*/
  535. return p.SKVMGuestDriver.RequestDeleteSnapshot(ctx, guest, task, params)
  536. }
  537. func (p *SPodDriver) BeforeDetachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, dev *models.SIsolatedDevice) error {
  538. ctrs, err := models.GetContainerManager().GetContainersByPod(guest.GetId())
  539. if err != nil {
  540. return errors.Wrapf(err, "get containers by pod %s", guest.GetId())
  541. }
  542. for _, ctr := range ctrs {
  543. ctrPtr := &ctr
  544. spec := ctrPtr.Spec
  545. devs := spec.Devices
  546. newDevs := make([]*api.ContainerDevice, 0)
  547. releasedDevs := make(map[string]models.ContainerReleasedDevice)
  548. for _, curDev := range devs {
  549. if curDev.IsolatedDevice == nil || curDev.IsolatedDevice.Id != dev.GetId() {
  550. tmpDev := curDev
  551. newDevs = append(newDevs, tmpDev)
  552. } else {
  553. releasedDevs[curDev.IsolatedDevice.Id] = *models.NewContainerReleasedDevice(curDev, dev.DevType, dev.Model)
  554. }
  555. }
  556. if err := ctrPtr.SaveReleasedDevices(ctx, userCred, releasedDevs); err != nil {
  557. return errors.Wrapf(err, "save release devices for container %s", ctr.GetId())
  558. }
  559. if _, err := db.Update(ctrPtr, func() error {
  560. ctrPtr.Spec.Devices = newDevs
  561. return nil
  562. }); err != nil {
  563. return errors.Wrapf(err, "update container %s devs", ctrPtr.GetId())
  564. }
  565. }
  566. return nil
  567. }
  568. func (p *SPodDriver) BeforeAttachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, dev *models.SIsolatedDevice) error {
  569. ctrs, err := models.GetContainerManager().GetContainersByPod(guest.GetId())
  570. if err != nil {
  571. return errors.Wrapf(err, "get containers by pod %s", guest.GetId())
  572. }
  573. for _, ctr := range ctrs {
  574. ctrPtr := &ctr
  575. if err := p.attachIsolatedDeviceToContainer(ctx, userCred, ctrPtr, dev); err != nil {
  576. return errors.Wrapf(err, "attach isolated device to container %s", ctr.GetId())
  577. }
  578. }
  579. return nil
  580. }
  581. func (p *SPodDriver) attachIsolatedDeviceToContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrPtr *models.SContainer, dev *models.SIsolatedDevice) error {
  582. rlsDevs, err := ctrPtr.GetReleasedDevices(ctx, userCred)
  583. if err != nil {
  584. return errors.Wrapf(err, "get release devices for container %s", ctrPtr.GetId())
  585. }
  586. spec := new(api.ContainerSpec)
  587. if err := jsonutils.Marshal(ctrPtr.Spec).Unmarshal(spec); err != nil {
  588. return errors.Wrap(err, "deep copy spec")
  589. }
  590. // attach it
  591. if spec.Devices == nil {
  592. spec.Devices = make([]*api.ContainerDevice, 0)
  593. }
  594. shouldUpdate := true
  595. for _, curDev := range spec.Devices {
  596. if curDev.IsolatedDevice == nil {
  597. continue
  598. }
  599. if curDev.IsolatedDevice.Id == dev.GetId() {
  600. shouldUpdate = false
  601. break
  602. }
  603. }
  604. if shouldUpdate {
  605. spec.Devices = append(spec.Devices, &api.ContainerDevice{
  606. Type: apis.CONTAINER_DEVICE_TYPE_ISOLATED_DEVICE,
  607. IsolatedDevice: &api.ContainerIsolatedDevice{
  608. Id: dev.GetId(),
  609. },
  610. })
  611. if _, err := db.Update(ctrPtr, func() error {
  612. ctrPtr.Spec = spec
  613. return nil
  614. }); err != nil {
  615. return errors.Wrapf(err, "update container %s devs", ctrPtr.GetId())
  616. }
  617. }
  618. for id, rlsDev := range rlsDevs {
  619. if rlsDev.IsolatedDevice == nil {
  620. continue
  621. }
  622. if rlsDev.DeviceModel == dev.Model && rlsDev.DeviceType == dev.DevType {
  623. delete(rlsDevs, id)
  624. if err := ctrPtr.SaveReleasedDevices(ctx, userCred, rlsDevs); err != nil {
  625. return errors.Wrapf(err, "save release devices for container %s", ctrPtr.GetId())
  626. }
  627. return nil
  628. }
  629. }
  630. return nil
  631. }