eip.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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 openstack
  15. import (
  16. "fmt"
  17. "net/url"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. api "yunion.io/x/cloudmux/pkg/apis/compute"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. "yunion.io/x/cloudmux/pkg/multicloud"
  25. )
  26. type SPortDetail struct {
  27. Status string `json:"status"`
  28. Name string `json:"name"`
  29. AdminStateUp bool `json:"admin_state_up"`
  30. NetworkId string `json:"network_id"`
  31. DeviceOwner string `json:"device_owner"`
  32. MacAddress string `json:"mac_address"`
  33. DeviceId string `json:"device_id"`
  34. }
  35. type SEipAddress struct {
  36. region *SRegion
  37. multicloud.SEipBase
  38. OpenStackTags
  39. RouterId string `json:"router_id"`
  40. Status string `json:"status"`
  41. Description string `json:"description"`
  42. Tags []string `json:"tags"`
  43. TenantId string `json:"tenant_id"`
  44. CreatedAt time.Time `json:"created_at"`
  45. UpdatedAt time.Time `json:"updated_at"`
  46. FloatingNetworkId string `json:"floating_network_id"`
  47. PortDetails SPortDetail `json:"port_details"`
  48. FixedIPAddress string `json:"fixed_ip_address"`
  49. FloatingIPAddress string `json:"floating_ip_address"`
  50. RevisionNumber int `json:"revision_number"`
  51. ProjectId string `json:"project_id"`
  52. PortId string `json:"port_id"`
  53. Id string `json:"id"`
  54. QosPolicyId string `json:"qos_policy_id"`
  55. }
  56. func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
  57. resource := fmt.Sprintf("/v2.0/floatingips/%s", eipId)
  58. resp, err := region.vpcGet(resource)
  59. if err != nil {
  60. return nil, err
  61. }
  62. eip := &SEipAddress{region: region}
  63. err = resp.Unmarshal(eip, "floatingip")
  64. if err != nil {
  65. return nil, errors.Wrap(err, "resp.Unmarshal")
  66. }
  67. return eip, nil
  68. }
  69. func (region *SRegion) GetEipByIp(ip string) (*SEipAddress, error) {
  70. eips, err := region.GetEips(ip)
  71. if err != nil {
  72. return nil, errors.Wrap(err, "GetEips")
  73. }
  74. if len(eips) == 1 {
  75. eips[0].region = region
  76. return &eips[0], nil
  77. }
  78. if len(eips) == 0 {
  79. return nil, cloudprovider.ErrNotFound
  80. }
  81. return nil, cloudprovider.ErrDuplicateId
  82. }
  83. func (region *SRegion) GetEips(ip string) ([]SEipAddress, error) {
  84. resource := "/v2.0/floatingips"
  85. eips := []SEipAddress{}
  86. query := url.Values{}
  87. if len(ip) > 0 {
  88. query.Set("floating_ip_address", ip)
  89. }
  90. for {
  91. part := struct {
  92. Floatingips []SEipAddress
  93. FloatingipsLinks SNextLinks
  94. }{}
  95. resp, err := region.vpcList(resource, query)
  96. if err != nil {
  97. return nil, errors.Wrap(err, "vpcList")
  98. }
  99. err = resp.Unmarshal(&part)
  100. if err != nil {
  101. return nil, errors.Wrap(err, "resp.Unmarshal")
  102. }
  103. eips = append(eips, part.Floatingips...)
  104. marker := part.FloatingipsLinks.GetNextMark()
  105. if len(marker) == 0 {
  106. break
  107. }
  108. query.Set("marker", marker)
  109. }
  110. return eips, nil
  111. }
  112. func (eip *SEipAddress) GetId() string {
  113. return eip.Id
  114. }
  115. func (eip *SEipAddress) GetName() string {
  116. return eip.FloatingIPAddress
  117. }
  118. func (eip *SEipAddress) GetGlobalId() string {
  119. return eip.Id
  120. }
  121. func (eip *SEipAddress) GetStatus() string {
  122. switch eip.Status {
  123. case "ACTIVE":
  124. return api.EIP_STATUS_READY
  125. case "DOWN": //实际是未绑定在机器上
  126. return api.EIP_STATUS_READY
  127. case "ERROR":
  128. return api.EIP_STATUS_UNKNOWN
  129. default:
  130. log.Errorf("Unknown eip %s status %s", eip.Id, eip.Status)
  131. return api.EIP_STATUS_UNKNOWN
  132. }
  133. }
  134. func (eip *SEipAddress) Refresh() error {
  135. _eip, err := eip.region.GetEip(eip.Id)
  136. if err != nil {
  137. return err
  138. }
  139. return jsonutils.Update(eip, _eip)
  140. }
  141. func (eip *SEipAddress) IsEmulated() bool {
  142. return false
  143. }
  144. func (eip *SEipAddress) GetIpAddr() string {
  145. return eip.FloatingIPAddress
  146. }
  147. func (eip *SEipAddress) GetMode() string {
  148. return api.EIP_MODE_STANDALONE_EIP
  149. }
  150. func (eip *SEipAddress) GetAssociationType() string {
  151. if len(eip.GetAssociationExternalId()) > 0 {
  152. return api.EIP_ASSOCIATE_TYPE_SERVER
  153. }
  154. return ""
  155. }
  156. func (eip *SEipAddress) GetAssociationExternalId() string {
  157. if len(eip.PortDetails.DeviceId) > 0 {
  158. return eip.PortDetails.DeviceId
  159. }
  160. if len(eip.PortId) > 0 {
  161. port, err := eip.region.GetPort(eip.PortId)
  162. if err != nil {
  163. log.Errorf("failed to get eip port %s info", eip.PortId)
  164. return ""
  165. }
  166. return port.DeviceID
  167. }
  168. return ""
  169. }
  170. func (eip *SEipAddress) GetBillingType() string {
  171. return ""
  172. }
  173. func (eip *SEipAddress) GetCreatedAt() time.Time {
  174. return eip.CreatedAt
  175. }
  176. func (eip *SEipAddress) GetExpiredAt() time.Time {
  177. return time.Time{}
  178. }
  179. func (eip *SEipAddress) Delete() error {
  180. return eip.region.DeleteEip(eip.Id)
  181. }
  182. func (eip *SEipAddress) GetBandwidth() int {
  183. return 0
  184. }
  185. func (eip *SEipAddress) GetINetworkId() string {
  186. networks, err := eip.region.GetNetworks(eip.FloatingNetworkId)
  187. if err != nil {
  188. log.Errorf("failed to find vpc id for eip %s(%s), error: %v", eip.FloatingIPAddress, eip.FloatingNetworkId, err)
  189. return ""
  190. }
  191. for _, network := range networks {
  192. for _, pool := range network.AllocationPools {
  193. if pool.Contains(eip.FloatingIPAddress) {
  194. network.AllocationPools = []AllocationPool{pool}
  195. return network.GetGlobalId()
  196. }
  197. }
  198. }
  199. log.Errorf("failed to find eip %s(%s) networkId", eip.FloatingIPAddress, eip.FloatingNetworkId)
  200. return ""
  201. }
  202. func (eip *SEipAddress) GetInternetChargeType() string {
  203. return api.EIP_CHARGE_TYPE_BY_TRAFFIC
  204. }
  205. func (eip *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error {
  206. return eip.region.AssociateEip(conf.InstanceId, eip.Id)
  207. }
  208. func (eip *SEipAddress) Dissociate() error {
  209. return eip.region.DisassociateEip(eip.Id)
  210. }
  211. func (eip *SEipAddress) ChangeBandwidth(bw int) error {
  212. return cloudprovider.ErrNotSupported
  213. }
  214. func (eip *SEipAddress) GetProjectId() string {
  215. return eip.ProjectId
  216. }
  217. func (region *SRegion) CreateEip(vpcId, networkId, ip string, projectId string) (*SEipAddress, error) {
  218. _, networkId = getNetworkId(networkId)
  219. params := map[string]map[string]string{
  220. "floatingip": map[string]string{
  221. "floating_network_id": vpcId,
  222. "subnet_id": networkId,
  223. },
  224. }
  225. if len(projectId) > 0 {
  226. params["floatingip"]["tenant_id"] = projectId
  227. }
  228. if len(ip) > 0 {
  229. params["floatingip"]["floating_ip_address"] = ip
  230. }
  231. resource := "/v2.0/floatingips"
  232. resp, err := region.vpcPost(resource, params)
  233. if err != nil {
  234. return nil, errors.Wrap(err, "vpcPost")
  235. }
  236. eip := &SEipAddress{region: region}
  237. err = resp.Unmarshal(eip, "floatingip")
  238. if err != nil {
  239. return nil, errors.Wrap(err, "resp.Unmarshal")
  240. }
  241. return eip, nil
  242. }
  243. func (region *SRegion) AssociateEip(instanceId, eipId string) error {
  244. instance, err := region.GetInstance(instanceId)
  245. if err != nil {
  246. return err
  247. }
  248. for networkName, address := range instance.Addresses {
  249. for i := 0; i < len(address); i++ {
  250. if instance.Addresses[networkName][i].Type == "fixed" {
  251. ports, err := region.GetPorts(instance.Addresses[networkName][i].MacAddr, "")
  252. if err != nil {
  253. return err
  254. }
  255. if len(ports) == 1 {
  256. params := map[string]map[string]string{
  257. "floatingip": {
  258. "port_id": ports[0].ID,
  259. },
  260. }
  261. resource := "/v2.0/floatingips/" + eipId
  262. _, err = region.vpcUpdate(resource, params)
  263. return err
  264. }
  265. if len(ports) == 0 {
  266. log.Errorf("failed to found port for instance nic %s(%s)", instance.Addresses[networkName][i].Addr, instance.Addresses[networkName][i].MacAddr)
  267. return cloudprovider.ErrNotFound
  268. }
  269. return cloudprovider.ErrDuplicateId
  270. }
  271. }
  272. }
  273. return fmt.Errorf("failed to found instnace %s nics for binding eip", instanceId)
  274. }
  275. func (region *SRegion) AssociateEipWithPortId(portid, eipId string) error {
  276. params := map[string]map[string]string{
  277. "floatingip": {
  278. "port_id": portid,
  279. },
  280. }
  281. _, err := region.vpcUpdate("/v2.0/floatingips/"+eipId, jsonutils.Marshal(params))
  282. return err
  283. }
  284. func (region *SRegion) DisassociateEip(eipId string) error {
  285. params, _ := jsonutils.Parse([]byte(`{
  286. "floatingip": {
  287. "port_id": null,
  288. },
  289. }`))
  290. _, err := region.vpcUpdate("/v2.0/floatingips/"+eipId, params)
  291. return err
  292. }
  293. func (region *SRegion) DeleteEip(eipId string) error {
  294. resource := "/v2.0/floatingips/" + eipId
  295. _, err := region.vpcDelete(resource)
  296. return err
  297. }