Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
packer
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kristopher Ruzic
packer
Commits
1428e6df
Commit
1428e6df
authored
Jun 12, 2015
by
Mitchell Hashimoto
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2218 from mitchellh/f-openstack
OpenStack uses official OpenStack client
parents
1da56993
92b6b5c3
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
407 additions
and
400 deletions
+407
-400
builder/openstack/access_config.go
builder/openstack/access_config.go
+89
-68
builder/openstack/access_config_test.go
builder/openstack/access_config_test.go
+0
-77
builder/openstack/artifact.go
builder/openstack/artifact.go
+4
-3
builder/openstack/builder.go
builder/openstack/builder.go
+7
-20
builder/openstack/builder_test.go
builder/openstack/builder_test.go
+0
-53
builder/openstack/run_config.go
builder/openstack/run_config.go
+15
-13
builder/openstack/server.go
builder/openstack/server.go
+12
-11
builder/openstack/ssh.go
builder/openstack/ssh.go
+40
-22
builder/openstack/step_allocate_ip.go
builder/openstack/step_allocate_ip.go
+50
-23
builder/openstack/step_create_image.go
builder/openstack/step_create_image.go
+27
-12
builder/openstack/step_key_pair.go
builder/openstack/step_key_pair.go
+30
-13
builder/openstack/step_load_flavor.go
builder/openstack/step_load_flavor.go
+61
-0
builder/openstack/step_run_source_server.go
builder/openstack/step_run_source_server.go
+43
-33
builder/openstack/step_wait_for_rackconnect.go
builder/openstack/step_wait_for_rackconnect.go
+15
-7
website/source/docs/builders/openstack.html.markdown
website/source/docs/builders/openstack.html.markdown
+14
-45
No files found.
builder/openstack/access_config.go
View file @
1428e6df
...
...
@@ -4,99 +4,120 @@ import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/template/interpolate"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
)
// AccessConfig is for common configuration related to openstack access
type
AccessConfig
struct
{
Username
string
`mapstructure:"username"`
Password
string
`mapstructure:"password"`
ApiKey
string
`mapstructure:"api_key"`
Project
string
`mapstructure:"project"`
Provider
string
`mapstructure:"provider"`
RawRegion
string
`mapstructure:"region"`
ProxyUrl
string
`mapstructure:"proxy_url"`
TenantId
string
`mapstructure:"tenant_id"`
Insecure
bool
`mapstructure:"insecure"`
Username
string
`mapstructure:"username"`
UserID
string
`mapstructure:"user_id"`
Password
string
`mapstructure:"password"`
APIKey
string
`mapstructure:"api_key"`
IdentityEndpoint
string
`mapstructure:"identity_endpoint"`
TenantID
string
`mapstructure:"tenant_id"`
TenantName
string
`mapstructure:"tenant_name"`
DomainID
string
`mapstructure:"domain_id"`
DomainName
string
`mapstructure:"domain_name"`
Insecure
bool
`mapstructure:"insecure"`
Region
string
`mapstructure:"region"`
EndpointType
string
`mapstructure:"endpoint_type"`
osClient
*
gophercloud
.
ProviderClient
}
// Auth returns a valid Auth object for access to openstack services, or
// an error if the authentication couldn't be resolved.
func
(
c
*
AccessConfig
)
Auth
()
(
gophercloud
.
AccessProvider
,
error
)
{
c
.
Username
=
common
.
ChooseString
(
c
.
Username
,
os
.
Getenv
(
"SDK_USERNAME"
),
os
.
Getenv
(
"OS_USERNAME"
))
c
.
Password
=
common
.
ChooseString
(
c
.
Password
,
os
.
Getenv
(
"SDK_PASSWORD"
),
os
.
Getenv
(
"OS_PASSWORD"
))
c
.
ApiKey
=
common
.
ChooseString
(
c
.
ApiKey
,
os
.
Getenv
(
"SDK_API_KEY"
))
c
.
Project
=
common
.
ChooseString
(
c
.
Project
,
os
.
Getenv
(
"SDK_PROJECT"
),
os
.
Getenv
(
"OS_TENANT_NAME"
))
c
.
Provider
=
common
.
ChooseString
(
c
.
Provider
,
os
.
Getenv
(
"SDK_PROVIDER"
),
os
.
Getenv
(
"OS_AUTH_URL"
))
c
.
RawRegion
=
common
.
ChooseString
(
c
.
RawRegion
,
os
.
Getenv
(
"SDK_REGION"
),
os
.
Getenv
(
"OS_REGION_NAME"
))
c
.
TenantId
=
common
.
ChooseString
(
c
.
TenantId
,
os
.
Getenv
(
"OS_TENANT_ID"
))
// OpenStack's auto-generated openrc.sh files do not append the suffix
// /tokens to the authentication URL. This ensures it is present when
// specifying the URL.
if
strings
.
Contains
(
c
.
Provider
,
"://"
)
&&
!
strings
.
HasSuffix
(
c
.
Provider
,
"/tokens"
)
{
c
.
Provider
+=
"/tokens"
func
(
c
*
AccessConfig
)
Prepare
(
ctx
*
interpolate
.
Context
)
[]
error
{
if
c
.
EndpointType
!=
"internal"
&&
c
.
EndpointType
!=
"internalURL"
&&
c
.
EndpointType
!=
"admin"
&&
c
.
EndpointType
!=
"adminURL"
&&
c
.
EndpointType
!=
"public"
&&
c
.
EndpointType
!=
"publicURL"
&&
c
.
EndpointType
!=
""
{
return
[]
error
{
fmt
.
Errorf
(
"Invalid endpoint type provided"
)}
}
authoptions
:=
gophercloud
.
AuthOptions
{
AllowReauth
:
true
,
ApiKey
:
c
.
ApiKey
,
TenantId
:
c
.
TenantId
,
TenantName
:
c
.
Project
,
Username
:
c
.
Username
,
Password
:
c
.
Password
,
if
c
.
Region
==
""
{
c
.
Region
=
os
.
Getenv
(
"OS_REGION_NAME"
)
}
default_transport
:=
&
http
.
Transport
{}
if
c
.
Insecure
{
cfg
:=
new
(
tls
.
Config
)
cfg
.
InsecureSkipVerify
=
true
default_transport
.
TLSClientConfig
=
cfg
// Legacy RackSpace stuff. We're keeping this around to keep things BC.
if
c
.
APIKey
==
""
{
c
.
APIKey
=
os
.
Getenv
(
"SDK_API_KEY"
)
}
if
c
.
Password
==
""
{
c
.
Password
=
os
.
Getenv
(
"SDK_PASSWORD"
)
}
if
c
.
Region
==
""
{
c
.
Region
=
os
.
Getenv
(
"SDK_REGION"
)
}
if
c
.
TenantName
==
""
{
c
.
TenantName
=
os
.
Getenv
(
"SDK_PROJECT"
)
}
if
c
.
Username
==
""
{
c
.
Username
=
os
.
Getenv
(
"SDK_USERNAME"
)
}
// For corporate networks it may be the case where we want our API calls
// to be sent through a separate HTTP proxy than external traffic.
if
c
.
ProxyUrl
!=
""
{
url
,
err
:=
url
.
Parse
(
c
.
ProxyUrl
)
if
err
!=
nil
{
return
nil
,
err
// Get as much as possible from the end
ao
,
_
:=
openstack
.
AuthOptionsFromEnv
()
// Override values if we have them in our config
overrides
:=
[]
struct
{
From
,
To
*
string
}{
{
&
c
.
Username
,
&
ao
.
Username
},
{
&
c
.
UserID
,
&
ao
.
UserID
},
{
&
c
.
Password
,
&
ao
.
Password
},
{
&
c
.
APIKey
,
&
ao
.
APIKey
},
{
&
c
.
IdentityEndpoint
,
&
ao
.
IdentityEndpoint
},
{
&
c
.
TenantID
,
&
ao
.
TenantID
},
{
&
c
.
TenantName
,
&
ao
.
TenantName
},
{
&
c
.
DomainID
,
&
ao
.
DomainID
},
{
&
c
.
DomainName
,
&
ao
.
DomainName
},
}
for
_
,
s
:=
range
overrides
{
if
*
s
.
From
!=
""
{
*
s
.
To
=
*
s
.
From
}
}
// The gophercloud.Context has a UseCustomClient method which
// would allow us to override with a new instance of http.Client.
default_transport
.
Proxy
=
http
.
ProxyURL
(
url
)
// Build the client itself
client
,
err
:=
openstack
.
NewClient
(
ao
.
IdentityEndpoint
)
if
err
!=
nil
{
return
[]
error
{
err
}
}
if
c
.
Insecure
||
c
.
ProxyUrl
!=
""
{
http
.
DefaultTransport
=
default_transport
// If we have insecure set, then create a custom HTTP client that
// ignores SSL errors.
if
c
.
Insecure
{
config
:=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
transport
:=
&
http
.
Transport
{
TLSClientConfig
:
config
}
client
.
HTTPClient
.
Transport
=
transport
}
return
gophercloud
.
Authenticate
(
c
.
Provider
,
authoptions
)
// Auth
err
=
openstack
.
Authenticate
(
client
,
ao
)
if
err
!=
nil
{
return
[]
error
{
err
}
}
c
.
osClient
=
client
return
nil
}
func
(
c
*
AccessConfig
)
Region
()
string
{
return
common
.
ChooseString
(
c
.
RawRegion
,
os
.
Getenv
(
"SDK_REGION"
),
os
.
Getenv
(
"OS_REGION_NAME"
))
func
(
c
*
AccessConfig
)
computeV2Client
()
(
*
gophercloud
.
ServiceClient
,
error
)
{
return
openstack
.
NewComputeV2
(
c
.
osClient
,
gophercloud
.
EndpointOpts
{
Region
:
c
.
Region
,
Availability
:
c
.
getEndpointType
(),
})
}
func
(
c
*
AccessConfig
)
Prepare
(
ctx
*
interpolate
.
Context
)
[]
error
{
errs
:=
make
([]
error
,
0
)
if
strings
.
HasPrefix
(
c
.
Provider
,
"rackspace"
)
{
if
c
.
Region
()
==
""
{
errs
=
append
(
errs
,
fmt
.
Errorf
(
"region must be specified when using rackspace"
))
}
func
(
c
*
AccessConfig
)
getEndpointType
()
gophercloud
.
Availability
{
if
c
.
EndpointType
==
"internal"
||
c
.
EndpointType
==
"internalURL"
{
return
gophercloud
.
AvailabilityInternal
}
if
len
(
errs
)
>
0
{
return
errs
if
c
.
EndpointType
==
"admin"
||
c
.
EndpointType
==
"adminURL"
{
return
gophercloud
.
AvailabilityAdmin
}
return
nil
return
gophercloud
.
AvailabilityPublic
}
builder/openstack/access_config_test.go
deleted
100644 → 0
View file @
1da56993
package
openstack
import
(
"os"
"testing"
)
func
init
()
{
// Clear out the openstack env vars so they don't
// affect our tests.
os
.
Setenv
(
"SDK_REGION"
,
""
)
os
.
Setenv
(
"OS_REGION_NAME"
,
""
)
}
func
testAccessConfig
()
*
AccessConfig
{
return
&
AccessConfig
{}
}
func
TestAccessConfigPrepare_NoRegion_Rackspace
(
t
*
testing
.
T
)
{
c
:=
testAccessConfig
()
c
.
Provider
=
"rackspace-us"
if
err
:=
c
.
Prepare
(
nil
);
err
==
nil
{
t
.
Fatalf
(
"shouldn't have err: %s"
,
err
)
}
}
func
TestAccessConfigRegionWithEmptyEnv
(
t
*
testing
.
T
)
{
c
:=
testAccessConfig
()
c
.
Prepare
(
nil
)
if
c
.
Region
()
!=
""
{
t
.
Fatalf
(
"Region should be empty"
)
}
}
func
TestAccessConfigRegionWithSdkRegionEnv
(
t
*
testing
.
T
)
{
c
:=
testAccessConfig
()
c
.
Prepare
(
nil
)
expectedRegion
:=
"sdk_region"
os
.
Setenv
(
"SDK_REGION"
,
expectedRegion
)
os
.
Setenv
(
"OS_REGION_NAME"
,
""
)
if
c
.
Region
()
!=
expectedRegion
{
t
.
Fatalf
(
"Region should be: %s"
,
expectedRegion
)
}
}
func
TestAccessConfigRegionWithOsRegionNameEnv
(
t
*
testing
.
T
)
{
c
:=
testAccessConfig
()
c
.
Prepare
(
nil
)
expectedRegion
:=
"os_region_name"
os
.
Setenv
(
"SDK_REGION"
,
""
)
os
.
Setenv
(
"OS_REGION_NAME"
,
expectedRegion
)
if
c
.
Region
()
!=
expectedRegion
{
t
.
Fatalf
(
"Region should be: %s"
,
expectedRegion
)
}
}
func
TestAccessConfigPrepare_NoRegion_PrivateCloud
(
t
*
testing
.
T
)
{
c
:=
testAccessConfig
()
c
.
Provider
=
"http://some-keystone-server:5000/v2.0"
if
err
:=
c
.
Prepare
(
nil
);
err
!=
nil
{
t
.
Fatalf
(
"shouldn't have err: %s"
,
err
)
}
}
func
TestAccessConfigPrepare_Region
(
t
*
testing
.
T
)
{
dfw
:=
"DFW"
c
:=
testAccessConfig
()
c
.
RawRegion
=
dfw
if
err
:=
c
.
Prepare
(
nil
);
err
!=
nil
{
t
.
Fatalf
(
"shouldn't have err: %s"
,
err
)
}
if
dfw
!=
c
.
Region
()
{
t
.
Fatalf
(
"Regions do not match: %s %s"
,
dfw
,
c
.
Region
())
}
}
builder/openstack/artifact.go
View file @
1428e6df
...
...
@@ -4,7 +4,8 @@ import (
"fmt"
"log"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
)
// Artifact is an artifact implementation that contains built images.
...
...
@@ -16,7 +17,7 @@ type Artifact struct {
BuilderIdValue
string
// OpenStack connection for performing API stuff.
C
onn
gophercloud
.
CloudServersProvider
C
lient
*
gophercloud
.
ServiceClient
}
func
(
a
*
Artifact
)
BuilderId
()
string
{
...
...
@@ -42,5 +43,5 @@ func (a *Artifact) State(name string) interface{} {
func
(
a
*
Artifact
)
Destroy
()
error
{
log
.
Printf
(
"Destroying image: %s"
,
a
.
ImageId
)
return
a
.
Conn
.
DeleteImageById
(
a
.
ImageId
)
return
images
.
Delete
(
a
.
Client
,
a
.
ImageId
)
.
ExtractErr
(
)
}
builder/openstack/builder.go
View file @
1428e6df
...
...
@@ -9,7 +9,6 @@ import (
"github.com/mitchellh/packer/common"
"log"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
...
...
@@ -55,40 +54,28 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func
(
b
*
Builder
)
Run
(
ui
packer
.
Ui
,
hook
packer
.
Hook
,
cache
packer
.
Cache
)
(
packer
.
Artifact
,
error
)
{
auth
,
err
:=
b
.
config
.
AccessConfig
.
Auth
()
computeClient
,
err
:=
b
.
config
.
computeV2Client
()
if
err
!=
nil
{
return
nil
,
err
}
//fetches the api requisites from gophercloud for the appropriate
//openstack variant
api
,
err
:=
gophercloud
.
PopulateApi
(
b
.
config
.
RunConfig
.
OpenstackProvider
)
if
err
!=
nil
{
return
nil
,
err
}
api
.
Region
=
b
.
config
.
AccessConfig
.
Region
()
csp
,
err
:=
gophercloud
.
ServersApi
(
auth
,
api
)
if
err
!=
nil
{
log
.
Printf
(
"Region: %s"
,
b
.
config
.
AccessConfig
.
Region
())
return
nil
,
err
return
nil
,
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
}
// Setup the state bag and initial state for the steps
state
:=
new
(
multistep
.
BasicStateBag
)
state
.
Put
(
"config"
,
b
.
config
)
state
.
Put
(
"csp"
,
csp
)
state
.
Put
(
"hook"
,
hook
)
state
.
Put
(
"ui"
,
ui
)
// Build the steps
steps
:=
[]
multistep
.
Step
{
&
StepLoadFlavor
{
Flavor
:
b
.
config
.
Flavor
,
},
&
StepKeyPair
{
Debug
:
b
.
config
.
PackerDebug
,
DebugKeyPath
:
fmt
.
Sprintf
(
"os_%s.pem"
,
b
.
config
.
PackerBuildName
),
},
&
StepRunSourceServer
{
Name
:
b
.
config
.
ImageName
,
Flavor
:
b
.
config
.
Flavor
,
SourceImage
:
b
.
config
.
SourceImage
,
SecurityGroups
:
b
.
config
.
SecurityGroups
,
Networks
:
b
.
config
.
Networks
,
...
...
@@ -101,7 +88,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
FloatingIp
:
b
.
config
.
FloatingIp
,
},
&
common
.
StepConnectSSH
{
SSHAddress
:
SSHAddress
(
c
sp
,
b
.
config
.
SSHInterface
,
b
.
config
.
SSHPort
),
SSHAddress
:
SSHAddress
(
c
omputeClient
,
b
.
config
.
SSHInterface
,
b
.
config
.
SSHPort
),
SSHConfig
:
SSHConfig
(
b
.
config
.
SSHUsername
),
SSHWaitTimeout
:
b
.
config
.
SSHTimeout
(),
},
...
...
@@ -135,7 +122,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
artifact
:=
&
Artifact
{
ImageId
:
state
.
Get
(
"image"
)
.
(
string
),
BuilderIdValue
:
BuilderId
,
C
onn
:
csp
,
C
lient
:
computeClient
,
}
return
artifact
,
nil
...
...
builder/openstack/builder_test.go
View file @
1428e6df
...
...
@@ -9,7 +9,6 @@ func testConfig() map[string]interface{} {
return
map
[
string
]
interface
{}{
"username"
:
"foo"
,
"password"
:
"bar"
,
"provider"
:
"foo"
,
"region"
:
"DFW"
,
"image_name"
:
"foo"
,
"source_image"
:
"foo"
,
...
...
@@ -40,55 +39,3 @@ func TestBuilder_Prepare_BadType(t *testing.T) {
t
.
Fatalf
(
"prepare should fail"
)
}
}
func
TestBuilderPrepare_ImageName
(
t
*
testing
.
T
)
{
var
b
Builder
config
:=
testConfig
()
// Test good
config
[
"image_name"
]
=
"foo"
warns
,
err
:=
b
.
Prepare
(
config
)
if
len
(
warns
)
>
0
{
t
.
Fatalf
(
"bad: %#v"
,
warns
)
}
if
err
!=
nil
{
t
.
Fatalf
(
"should not have error: %s"
,
err
)
}
// Test bad
config
[
"image_name"
]
=
"foo {{"
b
=
Builder
{}
warns
,
err
=
b
.
Prepare
(
config
)
if
len
(
warns
)
>
0
{
t
.
Fatalf
(
"bad: %#v"
,
warns
)
}
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
// Test bad
delete
(
config
,
"image_name"
)
b
=
Builder
{}
warns
,
err
=
b
.
Prepare
(
config
)
if
len
(
warns
)
>
0
{
t
.
Fatalf
(
"bad: %#v"
,
warns
)
}
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
func
TestBuilderPrepare_InvalidKey
(
t
*
testing
.
T
)
{
var
b
Builder
config
:=
testConfig
()
// Add a random key
config
[
"i_should_not_be_valid"
]
=
true
warns
,
err
:=
b
.
Prepare
(
config
)
if
len
(
warns
)
>
0
{
t
.
Fatalf
(
"bad: %#v"
,
warns
)
}
if
err
==
nil
{
t
.
Fatal
(
"should have error"
)
}
}
builder/openstack/run_config.go
View file @
1428e6df
...
...
@@ -11,19 +11,21 @@ import (
// RunConfig contains configuration for running an instance from a source
// image and details on how to access that launched image.
type
RunConfig
struct
{
SourceImage
string
`mapstructure:"source_image"`
Flavor
string
`mapstructure:"flavor"`
RawSSHTimeout
string
`mapstructure:"ssh_timeout"`
SSHUsername
string
`mapstructure:"ssh_username"`
SSHPort
int
`mapstructure:"ssh_port"`
SSHInterface
string
`mapstructure:"ssh_interface"`
OpenstackProvider
string
`mapstructure:"openstack_provider"`
UseFloatingIp
bool
`mapstructure:"use_floating_ip"`
RackconnectWait
bool
`mapstructure:"rackconnect_wait"`
FloatingIpPool
string
`mapstructure:"floating_ip_pool"`
FloatingIp
string
`mapstructure:"floating_ip"`
SecurityGroups
[]
string
`mapstructure:"security_groups"`
Networks
[]
string
`mapstructure:"networks"`
SourceImage
string
`mapstructure:"source_image"`
Flavor
string
`mapstructure:"flavor"`
RawSSHTimeout
string
`mapstructure:"ssh_timeout"`
SSHUsername
string
`mapstructure:"ssh_username"`
SSHPort
int
`mapstructure:"ssh_port"`
SSHInterface
string
`mapstructure:"ssh_interface"`
RackconnectWait
bool
`mapstructure:"rackconnect_wait"`
FloatingIpPool
string
`mapstructure:"floating_ip_pool"`
FloatingIp
string
`mapstructure:"floating_ip"`
SecurityGroups
[]
string
`mapstructure:"security_groups"`
Networks
[]
string
`mapstructure:"networks"`
// Not really used, but here for BC
OpenstackProvider
string
`mapstructure:"openstack_provider"`
UseFloatingIp
bool
`mapstructure:"use_floating_ip"`
// Unexported fields that are calculated from others
sshTimeout
time
.
Duration
...
...
builder/openstack/server.go
View file @
1428e6df
...
...
@@ -3,12 +3,12 @@ package openstack
import
(
"errors"
"fmt"
"github.com/mitchellh/multistep"
"github.com/racker/perigee"
"log"
"time"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
// StateRefreshFunc is a function type used for StateChangeConf that is
...
...
@@ -33,21 +33,22 @@ type StateChangeConf struct {
// ServerStateRefreshFunc returns a StateRefreshFunc that is used to watch
// an openstack server.
func
ServerStateRefreshFunc
(
csp
gophercloud
.
CloudServersProvider
,
s
*
gophercloud
.
Server
)
StateRefreshFunc
{
func
ServerStateRefreshFunc
(
client
*
gophercloud
.
ServiceClient
,
s
*
servers
.
Server
)
StateRefreshFunc
{
return
func
()
(
interface
{},
string
,
int
,
error
)
{
resp
,
err
:=
csp
.
ServerById
(
s
.
Id
)
serverNew
,
err
:=
servers
.
Get
(
client
,
s
.
ID
)
.
Extract
(
)
if
err
!=
nil
{
urce
,
ok
:=
err
.
(
*
perigee
.
UnexpectedResponseCodeError
)
if
ok
&&
(
urce
.
Actual
==
404
)
{
log
.
Printf
(
"404 on ServerStateRefresh, returning DELETED"
)
errCode
,
ok
:=
err
.
(
*
gophercloud
.
UnexpectedResponseCodeError
)
if
ok
&&
errCode
.
Actual
==
404
{
log
.
Printf
(
"[INFO] 404 on ServerStateRefresh, returning DELETED"
)
return
nil
,
"DELETED"
,
0
,
nil
}
else
{
log
.
Printf
(
"Error on ServerStateRefresh: %s"
,
err
)
log
.
Printf
(
"
[ERROR]
Error on ServerStateRefresh: %s"
,
err
)
return
nil
,
""
,
0
,
err
}
}
return
resp
,
resp
.
Status
,
resp
.
Progress
,
nil
return
serverNew
,
serverNew
.
Status
,
serverNew
.
Progress
,
nil
}
}
...
...
builder/openstack/ssh.go
View file @
1428e6df
...
...
@@ -3,49 +3,67 @@ package openstack
import
(
"errors"
"fmt"
"github.com/mitchellh/multistep"
"golang.org/x/crypto/ssh"
"log"
"time"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"golang.org/x/crypto/ssh"
)
// SSHAddress returns a function that can be given to the SSH communicator
// for determining the SSH address based on the server AccessIPv4 setting..
func
SSHAddress
(
csp
gophercloud
.
CloudServersProvider
,
sshinterface
string
,
port
int
)
func
(
multistep
.
StateBag
)
(
string
,
error
)
{
func
SSHAddress
(
client
*
gophercloud
.
ServiceClient
,
sshinterface
string
,
port
int
)
func
(
multistep
.
StateBag
)
(
string
,
error
)
{
return
func
(
state
multistep
.
StateBag
)
(
string
,
error
)
{
s
:=
state
.
Get
(
"server"
)
.
(
*
gophercloud
.
Server
)
s
:=
state
.
Get
(
"server"
)
.
(
*
servers
.
Server
)
if
ip
:=
state
.
Get
(
"access_ip"
)
.
(
gophercloud
.
FloatingIp
);
ip
.
Ip
!=
""
{
return
fmt
.
Sprintf
(
"%s:%d"
,
ip
.
Ip
,
port
),
nil
// If we have a floating IP, use that
ip
:=
state
.
Get
(
"access_ip"
)
.
(
*
floatingip
.
FloatingIP
)
if
ip
!=
nil
&&
ip
.
IP
!=
""
{
return
fmt
.
Sprintf
(
"%s:%d"
,
ip
.
IP
,
port
),
nil
}
ip_pools
,
err
:=
s
.
AllAddressPools
()
if
err
!=
nil
{
return
""
,
errors
.
New
(
"Error parsing SSH addresses"
)
if
s
.
AccessIPv4
!=
""
{
return
fmt
.
Sprintf
(
"%s:%d"
,
s
.
AccessIPv4
,
port
),
nil
}
for
pool
,
addresses
:=
range
ip_pools
{
if
sshinterface
!=
""
{
if
pool
!=
sshinterface
{
continue
}
// Get all the addresses associated with this server. This
// was taken directly from Terraform.
for
_
,
networkAddresses
:=
range
s
.
Addresses
{
elements
,
ok
:=
networkAddresses
.
([]
interface
{})
if
!
ok
{
log
.
Printf
(
"[ERROR] Unknown return type for address field: %#v"
,
networkAddresses
)
continue
}
if
pool
!=
""
{
for
_
,
address
:=
range
addresses
{
if
address
.
Addr
!=
""
&&
address
.
Version
==
4
{
return
fmt
.
Sprintf
(
"%s:%d"
,
address
.
Addr
,
port
),
nil
for
_
,
element
:=
range
elements
{
var
addr
string
address
:=
element
.
(
map
[
string
]
interface
{})
if
address
[
"OS-EXT-IPS:type"
]
==
"floating"
{
addr
=
address
[
"addr"
]
.
(
string
)
}
else
{
if
address
[
"version"
]
.
(
float64
)
==
4
{
addr
=
address
[
"addr"
]
.
(
string
)
}
}
if
addr
!=
""
{
return
fmt
.
Sprintf
(
"%s:%d"
,
addr
,
port
),
nil
}
}
}
serverState
,
err
:=
csp
.
ServerById
(
s
.
Id
)
s
,
err
:=
servers
.
Get
(
client
,
s
.
ID
)
.
Extract
()
if
err
!=
nil
{
return
""
,
err
}
state
.
Put
(
"server"
,
s
erverState
)
state
.
Put
(
"server"
,
s
)
time
.
Sleep
(
1
*
time
.
Second
)
return
""
,
errors
.
New
(
"couldn't determine IP address for server"
)
...
...
builder/openstack/step_allocate_ip.go
View file @
1428e6df
...
...
@@ -2,10 +2,11 @@ package openstack
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/
mitchellh/gophercloud-fork-40444fb
"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
"github.com/
rackspace/gophercloud/openstack/compute/v2/servers
"
)
type
StepAllocateIp
struct
{
...
...
@@ -15,53 +16,79 @@ type StepAllocateIp struct {
func
(
s
*
StepAllocateIp
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
csp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
server
:=
state
.
Get
(
"server"
)
.
(
*
gophercloud
.
Server
)
config
:=
state
.
Get
(
"config"
)
.
(
Config
)
server
:=
state
.
Get
(
"server"
)
.
(
*
servers
.
Server
)
// We need the v2 compute client
client
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
var
instanceIp
floatingip
.
FloatingIP
var
instanceIp
gophercloud
.
FloatingIp
// This is here in case we error out before putting instanceIp into the
// statebag below, because it is requested by Cleanup()
state
.
Put
(
"access_ip"
,
instanceIp
)
state
.
Put
(
"access_ip"
,
&
instanceIp
)
if
s
.
FloatingIp
!=
""
{
instanceIp
.
I
p
=
s
.
FloatingIp
instanceIp
.
I
P
=
s
.
FloatingIp
}
else
if
s
.
FloatingIpPool
!=
""
{
newIp
,
err
:=
csp
.
CreateFloatingIp
(
s
.
FloatingIpPool
)
newIp
,
err
:=
floatingip
.
Create
(
client
,
floatingip
.
CreateOpts
{
Pool
:
s
.
FloatingIpPool
,
})
.
Extract
()
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating floating ip from pool '%s'"
,
s
.
FloatingIpPool
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
instanceIp
=
newIp
ui
.
Say
(
fmt
.
Sprintf
(
"Created temporary floating IP %s..."
,
instanceIp
.
Ip
))
instanceIp
=
*
newIp
ui
.
Say
(
fmt
.
Sprintf
(
"Created temporary floating IP %s..."
,
instanceIp
.
IP
))
}
if
instanceIp
.
Ip
!=
""
{
if
err
:=
csp
.
AssociateFloatingIp
(
server
.
Id
,
instanceIp
);
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error associating floating IP %s with instance."
,
instanceIp
.
Ip
)
if
instanceIp
.
IP
!=
""
{
err
:=
floatingip
.
Associate
(
client
,
server
.
ID
,
instanceIp
.
IP
)
.
ExtractErr
()
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error associating floating IP %s with instance."
,
instanceIp
.
IP
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
else
{
ui
.
Say
(
fmt
.
Sprintf
(
"Added floating IP %s to instance..."
,
instanceIp
.
Ip
))
}
}
state
.
Put
(
"access_ip"
,
instanceIp
)
ui
.
Say
(
fmt
.
Sprintf
(
"Added floating IP %s to instance..."
,
instanceIp
.
IP
))
}
state
.
Put
(
"access_ip"
,
&
instanceIp
)
return
multistep
.
ActionContinue
}
func
(
s
*
StepAllocateIp
)
Cleanup
(
state
multistep
.
StateBag
)
{
config
:=
state
.
Get
(
"config"
)
.
(
Config
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
csp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
instanceIp
:=
state
.
Get
(
"access_ip"
)
.
(
gophercloud
.
FloatingIp
)
if
s
.
FloatingIpPool
!=
""
&&
instanceIp
.
Id
!=
0
{
if
err
:=
csp
.
DeleteFloatingIp
(
instanceIp
);
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error deleting temporary floating IP %s"
,
instanceIp
.
Ip
))
instanceIp
:=
state
.
Get
(
"access_ip"
)
.
(
*
floatingip
.
FloatingIP
)
// We need the v2 compute client
client
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error deleting temporary floating IP %s"
,
instanceIp
.
IP
))
return
}
if
s
.
FloatingIpPool
!=
""
&&
instanceIp
.
ID
!=
""
{
if
err
:=
floatingip
.
Delete
(
client
,
instanceIp
.
ID
)
.
ExtractErr
();
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error deleting temporary floating IP %s"
,
instanceIp
.
IP
))
return
}
ui
.
Say
(
fmt
.
Sprintf
(
"Deleted temporary floating IP %s"
,
instanceIp
.
Ip
))
ui
.
Say
(
fmt
.
Sprintf
(
"Deleted temporary floating IP %s"
,
instanceIp
.
IP
))
}
}
builder/openstack/step_create_image.go
View file @
1428e6df
...
...
@@ -2,28 +2,36 @@ package openstack
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
type
stepCreateImage
struct
{}
func
(
s
*
stepCreateImage
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
csp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
config
:=
state
.
Get
(
"config"
)
.
(
Config
)
server
:=
state
.
Get
(
"server"
)
.
(
*
gophercloud
.
Server
)
server
:=
state
.
Get
(
"server"
)
.
(
*
servers
.
Server
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// We need the v2 compute client
client
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
// Create the image
ui
.
Say
(
fmt
.
Sprintf
(
"Creating the image: %s"
,
config
.
ImageName
))
createOpts
:=
gophercloud
.
CreateImage
{
imageId
,
err
:=
servers
.
CreateImage
(
client
,
server
.
ID
,
servers
.
CreateImageOpts
{
Name
:
config
.
ImageName
,
}
imageId
,
err
:=
csp
.
CreateImage
(
server
.
Id
,
createOpts
)
})
.
ExtractImageID
()
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error creating image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
...
...
@@ -32,12 +40,12 @@ func (s *stepCreateImage) Run(state multistep.StateBag) multistep.StepAction {
}
// Set the Image ID in the state
ui
.
Say
(
fmt
.
Sprintf
(
"Image: %s"
,
imageId
))
ui
.
Message
(
fmt
.
Sprintf
(
"Image: %s"
,
imageId
))
state
.
Put
(
"image"
,
imageId
)
// Wait for the image to become ready
ui
.
Say
(
"Waiting for image to become ready..."
)
if
err
:=
WaitForImage
(
c
sp
,
imageId
);
err
!=
nil
{
if
err
:=
WaitForImage
(
c
lient
,
imageId
);
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error waiting for image: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
...
...
@@ -52,10 +60,17 @@ func (s *stepCreateImage) Cleanup(multistep.StateBag) {
}
// WaitForImage waits for the given Image ID to become ready.
func
WaitForImage
(
c
sp
gophercloud
.
CloudServersProvider
,
imageId
string
)
error
{
func
WaitForImage
(
c
lient
*
gophercloud
.
ServiceClient
,
imageId
string
)
error
{
for
{
image
,
err
:=
csp
.
ImageById
(
imageId
)
image
,
err
:=
images
.
Get
(
client
,
imageId
)
.
Extract
(
)
if
err
!=
nil
{
errCode
,
ok
:=
err
.
(
*
gophercloud
.
UnexpectedResponseCodeError
)
if
ok
&&
errCode
.
Actual
==
500
{
log
.
Printf
(
"[ERROR] 500 error received, will ignore and retry: %s"
,
err
)
time
.
Sleep
(
2
*
time
.
Second
)
continue
}
return
err
}
...
...
builder/openstack/step_key_pair.go
View file @
1428e6df
...
...
@@ -2,14 +2,13 @@ package openstack
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"log"
"os"
"runtime"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
)
type
StepKeyPair
struct
{
...
...
@@ -19,18 +18,28 @@ type StepKeyPair struct {
}
func
(
s
*
StepKeyPair
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
c
sp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
c
onfig
:=
state
.
Get
(
"config"
)
.
(
Config
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// We need the v2 compute client
computeClient
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
ui
.
Say
(
"Creating temporary keypair for this instance..."
)
keyName
:=
fmt
.
Sprintf
(
"packer %s"
,
uuid
.
TimeOrderedUUID
())
log
.
Printf
(
"temporary keypair name: %s"
,
keyName
)
keyResp
,
err
:=
csp
.
CreateKeyPair
(
gophercloud
.
NewKeyPair
{
Name
:
keyName
})
keypair
,
err
:=
keypairs
.
Create
(
computeClient
,
keypairs
.
CreateOpts
{
Name
:
keyName
,
})
.
Extract
()
if
err
!=
nil
{
state
.
Put
(
"error"
,
fmt
.
Errorf
(
"Error creating temporary keypair: %s"
,
err
))
return
multistep
.
ActionHalt
}
if
keyResp
.
PrivateKey
==
""
{
if
keypair
.
PrivateKey
==
""
{
state
.
Put
(
"error"
,
fmt
.
Errorf
(
"The temporary keypair returned was blank"
))
return
multistep
.
ActionHalt
}
...
...
@@ -47,7 +56,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
defer
f
.
Close
()
// Write the key out
if
_
,
err
:=
f
.
Write
([]
byte
(
key
Resp
.
PrivateKey
));
err
!=
nil
{
if
_
,
err
:=
f
.
Write
([]
byte
(
key
pair
.
PrivateKey
));
err
!=
nil
{
state
.
Put
(
"error"
,
fmt
.
Errorf
(
"Error saving debug key: %s"
,
err
))
return
multistep
.
ActionHalt
}
...
...
@@ -66,7 +75,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
// Set some state data for use in future steps
state
.
Put
(
"keyPair"
,
keyName
)
state
.
Put
(
"privateKey"
,
key
Resp
.
PrivateKey
)
state
.
Put
(
"privateKey"
,
key
pair
.
PrivateKey
)
return
multistep
.
ActionContinue
}
...
...
@@ -77,11 +86,19 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
return
}
c
sp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
c
onfig
:=
state
.
Get
(
"config"
)
.
(
Config
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// We need the v2 compute client
computeClient
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error cleaning up keypair. Please delete the key manually: %s"
,
s
.
keyName
))
return
}
ui
.
Say
(
"Deleting temporary keypair..."
)
err
:=
csp
.
DeleteKeyPair
(
s
.
keyName
)
err
=
keypairs
.
Delete
(
computeClient
,
s
.
keyName
)
.
ExtractErr
(
)
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error cleaning up keypair. Please delete the key manually: %s"
,
s
.
keyName
))
...
...
builder/openstack/step_load_flavor.go
0 → 100644
View file @
1428e6df
package
openstack
import
(
"fmt"
"log"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
)
// StepLoadFlavor gets the FlavorRef from a Flavor. It first assumes
// that the Flavor is a ref and verifies it. Otherwise, it tries to find
// the flavor by name.
type
StepLoadFlavor
struct
{
Flavor
string
}
func
(
s
*
StepLoadFlavor
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
config
:=
state
.
Get
(
"config"
)
.
(
Config
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// We need the v2 compute client
client
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
ui
.
Say
(
fmt
.
Sprintf
(
"Loading flavor: %s"
,
s
.
Flavor
))
log
.
Printf
(
"[INFO] Loading flavor by ID: %s"
,
s
.
Flavor
)
flavor
,
err
:=
flavors
.
Get
(
client
,
s
.
Flavor
)
.
Extract
()
if
err
!=
nil
{
log
.
Printf
(
"[ERROR] Failed to find flavor by ID: %s"
,
err
)
geterr
:=
err
log
.
Printf
(
"[INFO] Loading flavor by name: %s"
,
s
.
Flavor
)
id
,
err
:=
flavors
.
IDFromName
(
client
,
s
.
Flavor
)
if
err
!=
nil
{
log
.
Printf
(
"[ERROR] Failed to find flavor by name: %s"
,
err
)
err
=
fmt
.
Errorf
(
"Unable to find specified flavor by ID or name!
\n\n
"
+
"Error from ID lookup: %s
\n\n
"
+
"Error from name lookup: %s"
,
geterr
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
flavor
=
&
flavors
.
Flavor
{
ID
:
id
}
}
ui
.
Message
(
fmt
.
Sprintf
(
"Verified flavor. ID: %s"
,
flavor
.
ID
))
state
.
Put
(
"flavor_id"
,
flavor
.
ID
)
return
multistep
.
ActionContinue
}
func
(
s
*
StepLoadFlavor
)
Cleanup
(
state
multistep
.
StateBag
)
{
}
builder/openstack/step_run_source_server.go
View file @
1428e6df
...
...
@@ -2,51 +2,54 @@ package openstack
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
type
StepRunSourceServer
struct
{
Flavor
string
Name
string
SourceImage
string
SecurityGroups
[]
string
Networks
[]
string
server
*
gophercloud
.
Server
server
*
servers
.
Server
}
func
(
s
*
StepRunSourceServer
)
Run
(
state
multistep
.
StateBag
)
multistep
.
StepAction
{
csp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
config
:=
state
.
Get
(
"config"
)
.
(
Config
)
flavor
:=
state
.
Get
(
"flavor_id"
)
.
(
string
)
keyName
:=
state
.
Get
(
"keyPair"
)
.
(
string
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
//
XXX - validate image and flavor is available
securityGroups
:=
make
([]
map
[
string
]
interface
{},
len
(
s
.
SecurityGroups
))
for
i
,
groupName
:=
range
s
.
SecurityGroups
{
s
ecurityGroups
[
i
]
=
make
(
map
[
string
]
interface
{}
)
securityGroups
[
i
][
"name"
]
=
groupName
//
We need the v2 compute client
computeClient
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
s
tate
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
networks
:=
make
([]
gophercloud
.
NetworkConfig
,
len
(
s
.
Networks
))
networks
:=
make
([]
servers
.
Network
,
len
(
s
.
Networks
))
for
i
,
networkUuid
:=
range
s
.
Networks
{
networks
[
i
]
.
U
uid
=
networkUuid
networks
[
i
]
.
U
UID
=
networkUuid
}
server
:=
gophercloud
.
NewServer
{
Name
:
s
.
Name
,
ImageRef
:
s
.
SourceImage
,
FlavorRef
:
s
.
Flavor
,
KeyPairName
:
keyName
,
SecurityGroup
:
securityGroups
,
Networks
:
networks
,
}
serverResp
,
err
:=
csp
.
CreateServer
(
server
)
ui
.
Say
(
"Launching server..."
)
s
.
server
,
err
=
servers
.
Create
(
computeClient
,
keypairs
.
CreateOptsExt
{
CreateOptsBuilder
:
servers
.
CreateOpts
{
Name
:
s
.
Name
,
ImageRef
:
s
.
SourceImage
,
FlavorRef
:
flavor
,
SecurityGroups
:
s
.
SecurityGroups
,
Networks
:
networks
,
},
KeyName
:
keyName
,
})
.
Extract
()
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error launching source server: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
...
...
@@ -54,25 +57,25 @@ func (s *StepRunSourceServer) Run(state multistep.StateBag) multistep.StepAction
return
multistep
.
ActionHalt
}
s
.
server
,
err
=
csp
.
ServerById
(
serverResp
.
Id
)
log
.
Printf
(
"server id: %s"
,
s
.
server
.
I
d
)
ui
.
Message
(
fmt
.
Sprintf
(
"Server ID: %s"
,
s
.
server
.
ID
)
)
log
.
Printf
(
"server id: %s"
,
s
.
server
.
I
D
)
ui
.
Say
(
fmt
.
Sprintf
(
"Waiting for server (%s) to become ready..."
,
s
.
server
.
Id
)
)
ui
.
Say
(
"Waiting for server to become ready..."
)
stateChange
:=
StateChangeConf
{
Pending
:
[]
string
{
"BUILD"
},
Target
:
"ACTIVE"
,
Refresh
:
ServerStateRefreshFunc
(
c
sp
,
s
.
server
),
Refresh
:
ServerStateRefreshFunc
(
c
omputeClient
,
s
.
server
),
StepState
:
state
,
}
latestServer
,
err
:=
WaitForState
(
&
stateChange
)
if
err
!=
nil
{
err
:=
fmt
.
Errorf
(
"Error waiting for server (%s) to become ready: %s"
,
s
.
server
.
I
d
,
err
)
err
:=
fmt
.
Errorf
(
"Error waiting for server (%s) to become ready: %s"
,
s
.
server
.
I
D
,
err
)
state
.
Put
(
"error"
,
err
)
ui
.
Error
(
err
.
Error
())
return
multistep
.
ActionHalt
}
s
.
server
=
latestServer
.
(
*
gophercloud
.
Server
)
s
.
server
=
latestServer
.
(
*
servers
.
Server
)
state
.
Put
(
"server"
,
s
.
server
)
return
multistep
.
ActionContinue
...
...
@@ -83,18 +86,25 @@ func (s *StepRunSourceServer) Cleanup(state multistep.StateBag) {
return
}
c
sp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
c
onfig
:=
state
.
Get
(
"config"
)
.
(
Config
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
// We need the v2 compute client
computeClient
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error terminating server, may still be around: %s"
,
err
))
return
}
ui
.
Say
(
"Terminating the source server..."
)
if
err
:=
csp
.
DeleteServerById
(
s
.
server
.
Id
);
err
!=
nil
{
if
err
:=
servers
.
Delete
(
computeClient
,
s
.
server
.
ID
)
.
ExtractErr
(
);
err
!=
nil
{
ui
.
Error
(
fmt
.
Sprintf
(
"Error terminating server, may still be around: %s"
,
err
))
return
}
stateChange
:=
StateChangeConf
{
Pending
:
[]
string
{
"ACTIVE"
,
"BUILD"
,
"REBUILD"
,
"SUSPENDED"
},
Refresh
:
ServerStateRefreshFunc
(
c
sp
,
s
.
server
),
Refresh
:
ServerStateRefreshFunc
(
c
omputeClient
,
s
.
server
),
Target
:
"DELETED"
,
}
...
...
builder/openstack/step_wait_for_rackconnect.go
View file @
1428e6df
...
...
@@ -2,11 +2,11 @@ package openstack
import
(
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"time"
"github.com/mitchellh/gophercloud-fork-40444fb"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
type
StepWaitForRackConnect
struct
{
...
...
@@ -18,14 +18,22 @@ func (s *StepWaitForRackConnect) Run(state multistep.StateBag) multistep.StepAct
return
multistep
.
ActionContinue
}
c
sp
:=
state
.
Get
(
"csp"
)
.
(
gophercloud
.
CloudServersProvider
)
server
:=
state
.
Get
(
"server"
)
.
(
*
gophercloud
.
Server
)
c
onfig
:=
state
.
Get
(
"config"
)
.
(
Config
)
server
:=
state
.
Get
(
"server"
)
.
(
*
servers
.
Server
)
ui
:=
state
.
Get
(
"ui"
)
.
(
packer
.
Ui
)
ui
.
Say
(
fmt
.
Sprintf
(
"Waiting for server (%s) to become RackConnect ready..."
,
server
.
Id
))
// We need the v2 compute client
computeClient
,
err
:=
config
.
computeV2Client
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"Error initializing compute client: %s"
,
err
)
state
.
Put
(
"error"
,
err
)
return
multistep
.
ActionHalt
}
ui
.
Say
(
fmt
.
Sprintf
(
"Waiting for server (%s) to become RackConnect ready..."
,
server
.
ID
))
for
{
server
,
err
:=
csp
.
ServerById
(
server
.
Id
)
server
,
err
=
servers
.
Get
(
computeClient
,
server
.
ID
)
.
Extract
(
)
if
err
!=
nil
{
return
multistep
.
ActionHalt
}
...
...
website/source/docs/builders/openstack.html.markdown
View file @
1428e6df
...
...
@@ -29,28 +29,26 @@ each category, the available configuration keys are alphabetized.
### Required:
*
`flavor`
(string) - The ID or full URL for the desired flavor for the
*
`flavor`
(string) - The ID
, name,
or full URL for the desired flavor for the
server to be created.
*
`image_name`
(string) - The name of the resulting image.
*
`password`
(string) - The password used to connect to the OpenStack service.
If not specified, Packer will use the environment variables
`SDK_PASSWORD`
or
`OS_PASSWORD`
(in that order), if set.
*
`source_image`
(string) - The ID or full URL to the base image to use.
This is the image that will be used to launch a new server and provision it.
*
`username`
(string) - The username used to connect to the OpenStack service.
If not specified, Packer will use the environment variable
`OS_USERNAME`
, if set.
*
`password`
(string) - The password used to connect to the OpenStack service.
If not specified, Packer will use the environment variables
`
SDK_USERNAME`
or
`OS_USERNAME`
(in that order)
, if set.
`
OS_PASSWORD`
, if set.
### Optional:
*
`api_key`
(string) - The API key used to access OpenStack. Some OpenStack
installations require this.
If not specified, Packer will use the environment variables
`SDK_API_KEY`
, if set.
*
`floating_ip`
(string) - A specific floating IP to assign to this instance.
`use_floating_ip`
must also be set to true for this to have an affect.
...
...
@@ -65,32 +63,18 @@ each category, the available configuration keys are alphabetized.
*
`networks`
(array of strings) - A list of networks by UUID to attach
to this instance.
*
`openstack_provider`
(string) - A name of a provider that has a slightly
different API model. Currently supported values are "openstack" (default),
and "rackspace".
*
`project`
(string) - The project name to boot the instance into. Some
OpenStack installations require this.
If not specified, Packer will use the environment variables
`SDK_PROJECT`
or
`OS_TENANT_NAME`
(in that order), if set.
*
`provider`
(string) - The provider used to connect to the OpenStack service.
If not specified, Packer will use the environment variables
`SDK_PROVIDER`
or
`OS_AUTH_URL`
(in that order), if set.
For Rackspace this should be
`rackspace-us`
or
`rackspace-uk`
.
*
`proxy_url`
(string)
*
`tenant_id`
or
`tenant_name`
(string) - The tenant ID or name to boot the
instance into. Some OpenStack installations require this.
If not specified, Packer will use the environment variable
`OS_TENANT_NAME`
, if set.
*
`security_groups`
(array of strings) - A list of security groups by name
to add to this instance.
*
`region`
(string) - The name of the region, such as "DFW", in which
to launch the server to create the AMI.
If not specified, Packer will use the environment variables
`SDK_REGION`
or
`OS_REGION_NAME`
(in that order), if set.
For a
`provider`
of "rackspace", it is required to specify a region,
either using this option or with an environment variable. For other
providers, including a private cloud, specifying a region is optional.
If not specified, Packer will use the environment variable
`OS_REGION_NAME`
, if set.
*
`ssh_port`
(integer) - The port that SSH will be available on. Defaults to port
22.
...
...
@@ -106,9 +90,6 @@ each category, the available configuration keys are alphabetized.
useful for Rackspace are "public" or "private", and the default behavior is
to connect via whichever is returned first from the OpenStack API.
*
`tenant_id`
(string) - Tenant ID for accessing OpenStack if your
installation requires this.
*
`use_floating_ip`
(boolean) - Whether or not to use a floating IP for
the instance. Defaults to false.
...
...
@@ -124,10 +105,8 @@ Ubuntu 12.04 LTS (Precise Pangolin) on Rackspace OpenStack cloud offering.
```
javascript
{
"
type
"
:
"
openstack
"
,
"
username
"
:
""
,
"
api_key
"
:
""
,
"
openstack_provider
"
:
"
rackspace
"
,
"
provider
"
:
"
rackspace-us
"
,
"
username
"
:
"
foo
"
,
"
password
"
:
"
foo
"
,
"
region
"
:
"
DFW
"
,
"
ssh_username
"
:
"
root
"
,
"
image_name
"
:
"
Test image
"
,
...
...
@@ -160,13 +139,3 @@ script is setting environment variables like:
*
`OS_TENANT_ID`
*
`OS_USERNAME`
*
`OS_PASSWORD`
## Troubleshooting
*I get the error "Missing or incorrect provider"*
*
Verify your "username", "password" and "provider" settings.
*I get the error "Missing endpoint, or insufficient privileges to access endpoint"*
*
Verify your "region" setting.
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