Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
7fc8bcae
Commit
7fc8bcae
authored
Nov 12, 2015
by
Jan Safranek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
openstack: cache InstanceID and use it for volume management.
Hostnames are not related to server names or instance IDs in any way, don't use them when attaching/detaching volumes.
parent
e49f0de9
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
95 additions
and
65 deletions
+95
-65
openstack.go
pkg/cloudprovider/providers/openstack/openstack.go
+95
-65
No files found.
pkg/cloudprovider/providers/openstack/openstack.go
View file @
7fc8bcae
...
...
@@ -17,12 +17,13 @@ limitations under the License.
package
openstack
import
(
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
ossys
"os"
"regexp"
"strings"
"time"
...
...
@@ -48,6 +49,12 @@ import (
const
ProviderName
=
"openstack"
// metadataUrl is URL to OpenStack metadata server. It's hadrcoded IPv4
// link-local address as documented in "OpenStack Cloud Administrator Guide",
// chapter Compute - Networking with nova-network.
// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service
const
metadataUrl
=
"http://169.254.169.254/openstack/2012-08-10/meta_data.json"
var
ErrNotFound
=
errors
.
New
(
"Failed to find object"
)
var
ErrMultipleResults
=
errors
.
New
(
"Multiple results where only one expected"
)
var
ErrNoAddressFound
=
errors
.
New
(
"No address found for host"
)
...
...
@@ -86,6 +93,8 @@ type OpenStack struct {
provider
*
gophercloud
.
ProviderClient
region
string
lbOpts
LoadBalancerOpts
// InstanceID of the server where this OpenStack object is instantiated.
localInstanceID
string
}
type
Config
struct
{
...
...
@@ -140,17 +149,89 @@ func readConfig(config io.Reader) (Config, error) {
return
cfg
,
err
}
// parseMetadataUUID reads JSON from OpenStack metadata server and parses
// instance ID out of it.
func
parseMetadataUUID
(
jsonData
[]
byte
)
(
string
,
error
)
{
// We should receive an object with { 'uuid': '<uuid>' } and couple of other
// properties (which we ignore).
obj
:=
struct
{
UUID
string
}{}
err
:=
json
.
Unmarshal
(
jsonData
,
&
obj
)
if
err
!=
nil
{
return
""
,
err
}
uuid
:=
obj
.
UUID
if
uuid
==
""
{
err
=
fmt
.
Errorf
(
"cannot parse OpenStack metadata, got empty uuid"
)
return
""
,
err
}
return
uuid
,
nil
}
func
readInstanceID
()
(
string
,
error
)
{
// Try to find instance ID on the local filesystem (created by cloud-init)
const
instanceIDFile
=
"/var/lib/cloud/data/instance-id"
idBytes
,
err
:=
ioutil
.
ReadFile
(
instanceIDFile
)
if
err
==
nil
{
instanceID
:=
string
(
idBytes
)
instanceID
=
strings
.
TrimSpace
(
instanceID
)
glog
.
V
(
3
)
.
Infof
(
"Got instance id from %s: %s"
,
instanceIDFile
,
instanceID
)
if
instanceID
!=
""
{
return
instanceID
,
nil
}
// Fall through with empty instanceID and try metadata server.
}
glog
.
V
(
5
)
.
Infof
(
"Cannot read %s: '%v', trying metadata server"
,
instanceIDFile
,
err
)
// Try to get JSON from metdata server.
resp
,
err
:=
http
.
Get
(
metadataUrl
)
if
err
!=
nil
{
glog
.
V
(
3
)
.
Infof
(
"Cannot read %s: %v"
,
metadataUrl
,
err
)
return
""
,
err
}
if
resp
.
StatusCode
!=
200
{
err
=
fmt
.
Errorf
(
"got unexpected status code when reading metadata from %s: %s"
,
metadataUrl
,
resp
.
Status
)
glog
.
V
(
3
)
.
Infof
(
"%v"
,
err
)
return
""
,
err
}
defer
resp
.
Body
.
Close
()
bodyBytes
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
glog
.
V
(
3
)
.
Infof
(
"Cannot get HTTP response body from %s: %v"
,
metadataUrl
,
err
)
return
""
,
err
}
instanceID
,
err
:=
parseMetadataUUID
(
bodyBytes
)
if
err
!=
nil
{
glog
.
V
(
3
)
.
Infof
(
"Cannot parse instance ID from metadata from %s: %v"
,
metadataUrl
,
err
)
return
""
,
err
}
glog
.
V
(
3
)
.
Infof
(
"Got instance id from %s: %s"
,
metadataUrl
,
instanceID
)
return
instanceID
,
nil
}
func
newOpenStack
(
cfg
Config
)
(
*
OpenStack
,
error
)
{
provider
,
err
:=
openstack
.
AuthenticatedClient
(
cfg
.
toAuthOptions
())
if
err
!=
nil
{
return
nil
,
err
}
id
,
err
:=
readInstanceID
()
if
err
!=
nil
{
return
nil
,
err
}
os
:=
OpenStack
{
provider
:
provider
,
region
:
cfg
.
Global
.
Region
,
lbOpts
:
cfg
.
LoadBalancer
,
provider
:
provider
,
region
:
cfg
.
Global
.
Region
,
lbOpts
:
cfg
.
LoadBalancer
,
localInstanceID
:
id
,
}
return
&
os
,
nil
}
...
...
@@ -786,15 +867,10 @@ func (os *OpenStack) AttachDisk(diskName string) (string, error) {
glog
.
Errorf
(
"Unable to initialize nova client for region: %s"
,
os
.
region
)
return
""
,
err
}
compute_id
,
err
:=
os
.
getComputeIDbyHostname
(
cClient
)
if
err
!=
nil
||
len
(
compute_id
)
==
0
{
glog
.
Errorf
(
"Unable to get minion's id by minion's hostname"
)
return
""
,
err
}
if
len
(
disk
.
Attachments
)
>
0
&&
disk
.
Attachments
[
0
][
"server_id"
]
!=
nil
{
if
compute_id
==
disk
.
Attachments
[
0
][
"server_id"
]
{
glog
.
V
(
4
)
.
Infof
(
"Disk: %q is already attached to compute: %q"
,
diskName
,
compute_id
)
if
os
.
localInstanceID
==
disk
.
Attachments
[
0
][
"server_id"
]
{
glog
.
V
(
4
)
.
Infof
(
"Disk: %q is already attached to compute: %q"
,
diskName
,
os
.
localInstanceID
)
return
disk
.
ID
,
nil
}
else
{
errMsg
:=
fmt
.
Sprintf
(
"Disk %q is attached to a different compute: %q, should be detached before proceeding"
,
diskName
,
disk
.
Attachments
[
0
][
"server_id"
])
...
...
@@ -803,14 +879,14 @@ func (os *OpenStack) AttachDisk(diskName string) (string, error) {
}
}
// add read only flag here if possible spothanis
_
,
err
=
volumeattach
.
Create
(
cClient
,
compute_id
,
&
volumeattach
.
CreateOpts
{
_
,
err
=
volumeattach
.
Create
(
cClient
,
os
.
localInstanceID
,
&
volumeattach
.
CreateOpts
{
VolumeID
:
disk
.
ID
,
})
.
Extract
()
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to attach %s volume to %s compute"
,
diskName
,
compute_id
)
glog
.
Errorf
(
"Failed to attach %s volume to %s compute"
,
diskName
,
os
.
localInstanceID
)
return
""
,
err
}
glog
.
V
(
2
)
.
Infof
(
"Successfully attached %s volume to %s compute"
,
diskName
,
compute_id
)
glog
.
V
(
2
)
.
Infof
(
"Successfully attached %s volume to %s compute"
,
diskName
,
os
.
localInstanceID
)
return
disk
.
ID
,
nil
}
...
...
@@ -827,22 +903,17 @@ func (os *OpenStack) DetachDisk(partialDiskId string) error {
glog
.
Errorf
(
"Unable to initialize nova client for region: %s"
,
os
.
region
)
return
err
}
compute_id
,
err
:=
os
.
getComputeIDbyHostname
(
cClient
)
if
err
!=
nil
||
len
(
compute_id
)
==
0
{
glog
.
Errorf
(
"Unable to get compute id while detaching disk"
)
return
err
}
if
len
(
disk
.
Attachments
)
>
0
&&
disk
.
Attachments
[
0
][
"server_id"
]
!=
nil
&&
compute_id
==
disk
.
Attachments
[
0
][
"server_id"
]
{
if
len
(
disk
.
Attachments
)
>
0
&&
disk
.
Attachments
[
0
][
"server_id"
]
!=
nil
&&
os
.
localInstanceID
==
disk
.
Attachments
[
0
][
"server_id"
]
{
// This is a blocking call and effects kubelet's performance directly.
// We should consider kicking it out into a separate routine, if it is bad.
err
=
volumeattach
.
Delete
(
cClient
,
compute_id
,
disk
.
ID
)
.
ExtractErr
()
err
=
volumeattach
.
Delete
(
cClient
,
os
.
localInstanceID
,
disk
.
ID
)
.
ExtractErr
()
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to delete volume %s from compute %s attached %v"
,
disk
.
ID
,
compute_id
,
err
)
glog
.
Errorf
(
"Failed to delete volume %s from compute %s attached %v"
,
disk
.
ID
,
os
.
localInstanceID
,
err
)
return
err
}
glog
.
V
(
2
)
.
Infof
(
"Successfully detached volume: %s from compute: %s"
,
disk
.
ID
,
compute_id
)
glog
.
V
(
2
)
.
Infof
(
"Successfully detached volume: %s from compute: %s"
,
disk
.
ID
,
os
.
localInstanceID
)
}
else
{
errMsg
:=
fmt
.
Sprintf
(
"Disk: %s has no attachments or is not attached to compute: %s"
,
disk
.
Name
,
compute_id
)
errMsg
:=
fmt
.
Sprintf
(
"Disk: %s has no attachments or is not attached to compute: %s"
,
disk
.
Name
,
os
.
localInstanceID
)
glog
.
Errorf
(
errMsg
)
return
errors
.
New
(
errMsg
)
}
...
...
@@ -885,44 +956,3 @@ func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) {
}
return
volume
,
err
}
func
(
os
*
OpenStack
)
getComputeIDbyHostname
(
cClient
*
gophercloud
.
ServiceClient
)
(
string
,
error
)
{
hostname
,
err
:=
ossys
.
Hostname
()
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to get Minion's hostname: %v"
,
err
)
return
""
,
err
}
i
,
ok
:=
os
.
Instances
()
if
!
ok
{
glog
.
Errorf
(
"Unable to get instances"
)
return
""
,
errors
.
New
(
"Unable to get instances"
)
}
srvs
,
err
:=
i
.
List
(
"."
)
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to list servers: %v"
,
err
)
return
""
,
err
}
if
len
(
srvs
)
==
0
{
glog
.
Errorf
(
"Found no servers in the region"
)
return
""
,
errors
.
New
(
"Found no servers in the region"
)
}
glog
.
V
(
4
)
.
Infof
(
"Found servers: %v"
,
srvs
)
for
_
,
srvname
:=
range
srvs
{
server
,
err
:=
getServerByName
(
cClient
,
srvname
)
if
err
!=
nil
{
return
""
,
err
}
else
{
if
(
server
.
Metadata
[
"hostname"
]
!=
nil
&&
server
.
Metadata
[
"hostname"
]
==
hostname
)
||
(
len
(
server
.
Name
)
>
0
&&
server
.
Name
==
hostname
)
{
glog
.
V
(
4
)
.
Infof
(
"Found server: %s with host :%s"
,
server
.
Name
,
hostname
)
return
server
.
ID
,
nil
}
}
}
return
""
,
fmt
.
Errorf
(
"No server found matching hostname: %s"
,
hostname
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment