Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(provider/openstack) implement senlin zone policy and scheduler hints #2325

Merged
merged 1 commit into from
Jan 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ class ServerGroupParameters {
String sourceUserDataType
String sourceUserData
Map<String, String> tags
String resourceFilename
String floatingNetworkId
List<String> zones
Map<String, String> schedulerHints

static final ObjectMapper objectMapper = new ObjectMapper()

Expand Down Expand Up @@ -80,8 +80,8 @@ class ServerGroupParameters {
source_user_data : sourceUserData ?: null,
tags : objectMapper.writeValueAsString(tags ?: [:]) ?: null,
user_data : rawUserData ?: null,
resource_filename : resourceFilename ?: ServerGroupConstants.SUBTEMPLATE_FILE,
zones : zones?.join(',') ?: null,
scheduler_hints : objectMapper.writeValueAsString(schedulerHints ?: [:]) ?: null,
]
if (floatingNetworkId) {
params << [floating_network_id: floatingNetworkId]
Expand Down Expand Up @@ -118,8 +118,8 @@ class ServerGroupParameters {
tags: unescapePythonUnicodeJsonMap(params.get('tags') ?: '{}'),
sourceUserDataType: params.get('source_user_data_type'),
sourceUserData: params.get('source_user_data'),
resourceFilename: params.get('resource_filename'),
zones: unescapePythonUnicodeJsonList(params.get('zones')),
schedulerHints: unescapePythonUnicodeJsonMap(params.get('scheduler_hints') ?: '{}'),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package com.netflix.spinnaker.clouddriver.openstack.deploy.ops

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.netflix.spinnaker.clouddriver.openstack.deploy.description.servergroup.MemberData
import com.netflix.spinnaker.clouddriver.openstack.security.OpenstackCredentials
import org.openstack4j.model.network.ext.ListenerV2
Expand Down Expand Up @@ -69,8 +67,7 @@ trait StackPoolMemberAware {
* @param memberData
* @return
*/
String buildPoolMemberTemplate(List<MemberData> memberData) {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory())
Map buildPoolMemberTemplate(List<MemberData> memberData) {
Map<String, Object> parameters = [address: [type: "string", description: "Server address for autoscaling group resource"]]
Map<String, Object> resources = memberData.collectEntries {
[
Expand All @@ -90,6 +87,6 @@ trait StackPoolMemberAware {
description : "Pool members for autoscaling group resource",
parameters : parameters,
resources : resources]
mapper.writeValueAsString(memberTemplate)
return memberTemplate
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,35 +96,28 @@ abstract class AbstractStackUpdateOpenstackAtomicOperation implements AtomicOper
//pre update ops
preUpdate(stack)

//we need to store subtemplate in server group output from create, as it is required to do an update and there is no native way of
//obtaining it from a stack
String resourceFileName = stack.parameters?.get(ServerGroupConstants.SUBTEMPLATE_FILENAME)

if (resourceFileName) {
List<Map<String, Object>> outputs = stack.outputs
String resourceSubtemplate = outputs.find { m -> m.get("output_key") == ServerGroupConstants.SUBTEMPLATE_OUTPUT }.get("output_value")
String memberTemplate = outputs.find { m -> m.get("output_key") == ServerGroupConstants.MEMBERTEMPLATE_OUTPUT }.get("output_value")
task.updateStatus phaseName, "Successfully fetched server group $foundServerGroupName"

//get the current template from the stack
task.updateStatus phaseName, "Fetching current template for server group $foundServerGroupName"
String template = provider.getHeatTemplate(description.region, stack.name, stack.id)
task.updateStatus phaseName, "Successfully fetched current template for server group $foundServerGroupName"

Map<String, String> templateMap = [(resourceFileName): resourceSubtemplate]
if (memberTemplate) {
templateMap << [(ServerGroupConstants.MEMBERTEMPLATE_FILE): memberTemplate]
}

//update stack
task.updateStatus phaseName, "Updating server group $stack.name"
provider.updateStack(description.region, stack.name, stack.id, template, templateMap, buildServerGroupParameters(stack), stack.tags)
task.updateStatus phaseName, "Successfully updated server group $stack.name"
} else {
task.updateStatus phaseName, "Missing resource filename in parameters list- ${stack.parameters}"
throw new OpenstackOperationException("Missing resource filename in HEAT template")
String resourceFileName = ServerGroupConstants.SUBTEMPLATE_FILE

List<Map<String, Object>> outputs = stack.outputs
String resourceSubtemplate = outputs.find { m -> m.get("output_key") == ServerGroupConstants.SUBTEMPLATE_OUTPUT }.get("output_value")
String memberTemplate = outputs.find { m -> m.get("output_key") == ServerGroupConstants.MEMBERTEMPLATE_OUTPUT }.get("output_value")
task.updateStatus phaseName, "Successfully fetched server group $foundServerGroupName"

//get the current template from the stack
task.updateStatus phaseName, "Fetching current template for server group $foundServerGroupName"
String template = provider.getHeatTemplate(description.region, stack.name, stack.id)
task.updateStatus phaseName, "Successfully fetched current template for server group $foundServerGroupName"

Map<String, String> templateMap = [(resourceFileName): resourceSubtemplate]
if (memberTemplate) {
templateMap << [(ServerGroupConstants.MEMBERTEMPLATE_FILE): memberTemplate]
}

//update stack
task.updateStatus phaseName, "Updating server group $stack.name"
provider.updateStack(description.region, stack.name, stack.id, template, templateMap, buildServerGroupParameters(stack), stack.tags)
task.updateStatus phaseName, "Successfully updated server group $stack.name"

//post update ops
postUpdate(stack)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ class CloneOpenstackAtomicOperation implements AtomicOperation<DeploymentResult>
scaleup = description.serverGroupParameters?.scaleup ?: ancestorParams.scaleup
scaledown = description.serverGroupParameters?.scaledown ?: ancestorParams.scaledown
tags = description.serverGroupParameters?.tags ?: ancestorParams.tags
resourceFilename = description.serverGroupParameters?.resourceFilename ?: ancestorParams.resourceFilename

// Lack of floatingNetworkId means to not set one, so can't pull this value from the ancestorParams
floatingNetworkId = description.serverGroupParameters?.floatingNetworkId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.netflix.spinnaker.clouddriver.openstack.deploy.ops.servergroup

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.netflix.spinnaker.clouddriver.deploy.DeploymentResult
import com.netflix.spinnaker.clouddriver.openstack.client.OpenstackClientProvider
import com.netflix.spinnaker.clouddriver.openstack.deploy.OpenstackServerGroupNameResolver
Expand Down Expand Up @@ -56,6 +58,8 @@ class DeployOpenstackAtomicOperation implements TaskStatusAware, AtomicOperation

static final Map<String, String> templateMap = new ConcurrentHashMap<>()

private ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory())

DeployOpenstackAtomicOperation(DeployOpenstackAtomicOperationDescription description) {
this.description = description
}
Expand Down Expand Up @@ -115,46 +119,53 @@ class DeployOpenstackAtomicOperation implements TaskStatusAware, AtomicOperation
def stackName = serverGroupNameResolver.resolveNextServerGroupName(description.application, description.stack, description.freeFormDetails, false)
task.updateStatus BASE_PHASE, "Heat stack name chosen to be ${stackName}."

Map<String, String> subtemplates = [:]
String template = getTemplateFile(ServerGroupConstants.TEMPLATE_FILE)
String resourceFilename = ServerGroupConstants.SUBTEMPLATE_FILE
Map<String, Map> templates = [
main: objectMapper.readValue(getTemplateFile(ServerGroupConstants.TEMPLATE_FILE), Map)
]

if (description.serverGroupParameters.floatingNetworkId) {
template = getTemplateFile(ServerGroupConstants.TEMPLATE_FILE_FLOAT)
templates.main.parameters.floating_network_id = [type: "string", description: "Network used to allocate a floating IP for each server."]
templates.main.resources.servergroup.properties.resource.properties.floating_network_id = [get_param: "floating_network_id"]

}
if (description.serverGroupParameters.loadBalancers && !description.serverGroupParameters.loadBalancers.isEmpty()) {
//look up all load balancer listeners -> pool ids and internal ports
task.updateStatus BASE_PHASE, "Getting load balancer details for load balancers $description.serverGroupParameters.loadBalancers..."
List<MemberData> memberDataList = buildMemberData(description.credentials, description.region, description.serverGroupParameters.subnetId, description.serverGroupParameters.loadBalancers, this.&parseListenerKey)
task.updateStatus BASE_PHASE, "Finished getting load balancer details for load balancers $description.serverGroupParameters.loadBalancers."

templates[ServerGroupConstants.SUBTEMPLATE_FILE] = objectMapper.readValue(getTemplateFile(ServerGroupConstants.SUBTEMPLATE_FILE), Map)
//check for floating ip
if (description.serverGroupParameters.floatingNetworkId) {
resourceFilename = ServerGroupConstants.SUBTEMPLATE_FILE_FLOAT
templates[ServerGroupConstants.SUBTEMPLATE_FILE].parameters.floating_network_id = [type: "string", description: "Network used to allocate a floating IP for each server."]
templates[ServerGroupConstants.SUBTEMPLATE_FILE].resources.server_floating_ip = [
type: "OS::Neutron::FloatingIP",
properties: [
floating_network_id: [get_param: "floating_network_id"],
port_id: [get_attr: ["server", "addresses", [get_param: "network_id"], 0, "port"]]
]
]
}

task.updateStatus BASE_PHASE, "Loading lbaas subtemplates..."
String subtemplate = getTemplateFile(resourceFilename)
if (subtemplate) {
subtemplates << [(resourceFilename): subtemplate]
if (subtemplate.contains(ServerGroupConstants.MEMBERTEMPLATE_FILE)) {
subtemplates << [(ServerGroupConstants.MEMBERTEMPLATE_FILE): buildPoolMemberTemplate(memberDataList)]
}
if (objectMapper.writeValueAsString(templates[ServerGroupConstants.SUBTEMPLATE_FILE]).contains(ServerGroupConstants.MEMBERTEMPLATE_FILE)) {
templates[ServerGroupConstants.MEMBERTEMPLATE_FILE] = buildPoolMemberTemplate(memberDataList)
}
task.updateStatus BASE_PHASE, "Finished loading lbaas templates."
} else {
task.updateStatus BASE_PHASE, "Loading subtemplates..."

//check for floating ip
templates[ServerGroupConstants.SUBTEMPLATE_FILE] = objectMapper.readValue(getTemplateFile(ServerGroupConstants.SUBTEMPLATE_SERVER_FILE), Map)
if (description.serverGroupParameters.floatingNetworkId) {
resourceFilename = ServerGroupConstants.SUBTEMPLATE_SERVER_FILE_FLOAT
} else {
resourceFilename = ServerGroupConstants.SUBTEMPLATE_SERVER_FILE
}

String subtemplate = getTemplateFile(resourceFilename)
if (subtemplate) {
subtemplates << [(resourceFilename): subtemplate]
templates[ServerGroupConstants.SUBTEMPLATE_FILE].parameters.floating_network_id = [type: "string", description: "Network used to allocate a floating IP for each server."]
templates[ServerGroupConstants.SUBTEMPLATE_FILE].resources.server_floating_ip = [
type: "OS::Neutron::FloatingIP",
properties: [
floating_network_id: [get_param: "floating_network_id"],
port_id: [get_attr: ["server", "addresses", [get_param: "network_id"], 0, "port"]]
]
]
}
task.updateStatus BASE_PHASE, "Finished loading templates."
}
Expand All @@ -166,15 +177,24 @@ class DeployOpenstackAtomicOperation implements TaskStatusAware, AtomicOperation

String userData = getUserData(provider, stackName)

if (description.serverGroupParameters.zones) {
task.updateStatus BASE_PHASE, "Creating zone policy for ${description.serverGroupParameters.zones.size()} zones"
addZonePlacementPolicy(description.serverGroupParameters.zones, templates.main, templates[ServerGroupConstants.SUBTEMPLATE_FILE])
}

task.updateStatus BASE_PHASE, "Creating heat stack $stackName..."
ServerGroupParameters params = description.serverGroupParameters.identity {
it.networkId = subnet.networkId
it.rawUserData = userData
it.sourceUserDataType = description.userDataType
it.sourceUserData = description.userData
it.resourceFilename = resourceFilename
it
}

def template = objectMapper.writeValueAsString(templates.main)
//drop the primary template and convert everything to string
def subtemplates = (Map<String, String>) templates.findAll { it.key != "main"}.collectEntries {k, v -> [(k): objectMapper.writeValueAsString(v)]}

provider.deploy(description.region, stackName, template, subtemplates, params,
description.disableRollback, description.timeoutMins, description.serverGroupParameters.loadBalancers)
task.updateStatus BASE_PHASE, "Finished creating heat stack $stackName."
Expand Down Expand Up @@ -228,4 +248,38 @@ class DeployOpenstackAtomicOperation implements TaskStatusAware, AtomicOperation
template ?: ""
}
}

private static void addZonePlacementPolicy(List<String> zones, Map mainTemplate, Map resourceTemplate) {
def placementList = zones.collect { zone ->
[
name: zone,
weight: 100
]
}
mainTemplate.resources.zone_policy = [
type: "OS::Senlin::Policy",
properties: [
type: "senlin.policy.zone_placement",
version: "1.0",
properties: [
regions: placementList
]
]
]
mainTemplate.resources.zone_policy_group = [
type: "OS::Nova::ServerGroup",
properties: [
policies: [
[
get_resource: "zone_policy"
]
]
]
]
resourceTemplate.resources.server.properties.scheduler_hints = [
group: [
get_resource: "zone_policy_group"
]
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ class ServerGroupConstants {

//this is the name of the subtemplate referenced by the template,
//and needs to be loaded into memory as a String
final static String SUBTEMPLATE_FILE = "${SUBTEMPLATE_OUTPUT}.yaml"
final static String SUBTEMPLATE_FILE = "${SUBTEMPLATE_OUTPUT}.yaml".toString()
//with floating ip for each instance
final static String SUBTEMPLATE_FILE_FLOAT = "${SUBTEMPLATE_OUTPUT_FLOAT}.yaml"
final static String SUBTEMPLATE_FILE_FLOAT = "${SUBTEMPLATE_OUTPUT_FLOAT}.yaml".toString()

//this is the name of the member template referenced by the subtemplate,
//and is contructed on the fly
final static String MEMBERTEMPLATE_FILE = "${MEMBERTEMPLATE_OUTPUT}.yaml"
final static String MEMBERTEMPLATE_FILE = "${MEMBERTEMPLATE_OUTPUT}.yaml".toString()
}
18 changes: 12 additions & 6 deletions clouddriver-openstack/src/main/resources/servergroup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ parameters:
type: comma_delimited_list
description: Comma-separated string of load balancers to associate to the stack. This is not used in the stack and is defined for auditing purposes.
default: []
zones:
type: comma_delimited_list
description: Comma-separated string of availability zones
default: []
security_groups:
type: comma_delimited_list
description: Comma-separated string of security groups to use
Expand Down Expand Up @@ -81,9 +85,10 @@ parameters:
type: string
description: Raw base64-encoded string that will execute upon server boot, if cloud-init is installed
default: ""
resource_filename:
type: string
description: Member resource file name (i.e. servergroup_resource.yaml)
scheduler_hints:
type: json
description: Key/Value pairs in json format for scheduler_hints
default: {}
resources:
servergroup:
type: OS::Heat::AutoScalingGroup
Expand All @@ -92,7 +97,7 @@ resources:
max_size: {get_param: max_size}
desired_capacity: {get_param: desired_size}
resource:
type: {get_param: resource_filename}
type: servergroup_resource.yaml
properties:
flavor: {get_param: flavor}
image: {get_param: image}
Expand All @@ -107,6 +112,7 @@ resources:
security_groups: {get_param: security_groups}
subnet_id: {get_param: subnet_id}
user_data: {get_param: user_data}
scheduler_hints: {get_param: scheduler_hints}
web_server_scaleup_policy:
type: OS::Heat::ScalingPolicy
properties:
Expand Down Expand Up @@ -153,8 +159,8 @@ outputs:
# we need to store subtemplate in servergroup output from create, as it is required to do an update and there is no native way
# of obtaining it from a stack
servergroup_resource:
description: resource_filename template value
value: {get_file: {get_param: resource_filename} }
description: servergroup_resource.yaml template value
value: {get_file: servergroup_resource.yaml }
# we need to store subtemplate in servergroup output from create, as it is required to do an update and there is no native way
# of obtaining it from a stack
servergroup_resource_member:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ parameters:
user_data:
type: string
description: String that will execute upon server boot, if cloud-init is installed.
scheduler_hints:
type: json
description: Key/Value pairs in json format for scheduler_hints
default: {}
resources:
server:
type: OS::Nova::Server
Expand All @@ -33,6 +37,7 @@ resources:
- subnet: {get_param: subnet_id}
security_groups: {get_param: security_groups}
user_data: {get_param: user_data}
scheduler_hints: {get_param: scheduler_hints}
user_data_format: RAW
member:
type: OS::Heat::ResourceGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ parameters:
user_data:
type: string
description: Raw base64-encoded string that will execute upon server boot, if cloud-init is installed.
scheduler_hints:
type: json
description: Key/Value pairs in json format for scheduler_hints
default: {}
resources:
server:
type: OS::Nova::Server
Expand All @@ -33,6 +37,7 @@ resources:
- subnet: {get_param: subnet_id}
security_groups: {get_param: security_groups}
user_data: {get_param: user_data}
scheduler_hints: {get_param: scheduler_hints}
user_data_format: RAW

outputs:
Expand Down
Loading