From 61801a0ca8a2607b5d922e51d4c86dd93082847a Mon Sep 17 00:00:00 2001 From: Georgy Okrokvertskhov Date: Fri, 15 Feb 2013 06:04:29 -0800 Subject: [PATCH] 1. Added support of CloudFormation templates. Made a simple interface to build template. Stan can work here to redesign template.py 2. Added calls of drivers. Now heat is called from cmd instead of client. Should be rewritten. 3. ActiveDirectory makes a static template. Need to rewrite this with working with actual parameters. --- windc/heat_run | 5 + windc/tests/manual/createServiceParameters | 2 +- windc/tools/pip-requires | 2 +- windc/windc/adapters/openstack.py | 19 ++++ windc/windc/core/builder.py | 4 + windc/windc/core/builders/ActiveDirectory.py | 61 +++++++++++ windc/windc/core/change_events.py | 11 +- windc/windc/core/commands.py | 33 ++++++ windc/windc/core/templates.py | 107 +++++++++++++++++++ windc/windc/drivers/command_executor.py | 37 +++++++ windc/windc/drivers/openstack_heat.py | 38 +++++++ 11 files changed, 314 insertions(+), 5 deletions(-) create mode 100755 windc/heat_run create mode 100644 windc/windc/adapters/openstack.py create mode 100644 windc/windc/core/commands.py create mode 100644 windc/windc/core/templates.py create mode 100644 windc/windc/drivers/command_executor.py create mode 100644 windc/windc/drivers/openstack_heat.py diff --git a/windc/heat_run b/windc/heat_run new file mode 100755 index 000000000..69e418299 --- /dev/null +++ b/windc/heat_run @@ -0,0 +1,5 @@ +#!/bin/bash + +source openrc.sh +heat "$@" + diff --git a/windc/tests/manual/createServiceParameters b/windc/tests/manual/createServiceParameters index 6e9817c93..0954f9255 100644 --- a/windc/tests/manual/createServiceParameters +++ b/windc/tests/manual/createServiceParameters @@ -4,5 +4,5 @@ "domain": "ACME.cloud", "AdminUser": "Admin", "AdminPassword": "StrongPassword", -"DomainControllerNames": ["APP-AD001","APP-AD002"] +"DomainControllerNames": ["AD-DC001"] } diff --git a/windc/tools/pip-requires b/windc/tools/pip-requires index 0cb916b44..70f7e25c3 100644 --- a/windc/tools/pip-requires +++ b/windc/tools/pip-requires @@ -15,7 +15,7 @@ sqlalchemy-migrate>=0.7.2 httplib2 kombu iso8601>=0.1.4 - +PyChef # For paste.util.template used in keystone.common.template Paste diff --git a/windc/windc/adapters/openstack.py b/windc/windc/adapters/openstack.py new file mode 100644 index 000000000..9ca6733da --- /dev/null +++ b/windc/windc/adapters/openstack.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from heatclient import Client + diff --git a/windc/windc/core/builder.py b/windc/windc/core/builder.py index 2dc68e8c5..15a14e6bf 100644 --- a/windc/windc/core/builder.py +++ b/windc/windc/core/builder.py @@ -29,5 +29,9 @@ class Builder: def build(self, context, event, data): pass +def create_context(): + context = {} + context['commands']=[] + return context diff --git a/windc/windc/core/builders/ActiveDirectory.py b/windc/windc/core/builders/ActiveDirectory.py index 7181329a8..703703b29 100644 --- a/windc/windc/core/builders/ActiveDirectory.py +++ b/windc/windc/core/builders/ActiveDirectory.py @@ -17,11 +17,14 @@ import logging +import uuid LOG = logging.getLogger(__name__) from windc.core.builder import Builder from windc.core import change_events as events from windc.db import api as db_api +from windc.core.templates import Template +from windc.core import commands as command_api class ActiveDirectory(Builder): def __init__(self): @@ -35,6 +38,8 @@ class ActiveDirectory(Builder): LOG.info ("Got service change event. Analysing..") if self.do_analysis(context, event, dc): self.plan_changes(context, event, dc) + + self.submit_commands(context, event, dc) else: LOG.debug("Not in my scope. Skip event.") pass @@ -44,10 +49,66 @@ class ActiveDirectory(Builder): zones = data['zones'] if data['type'] == self.type and len(zones) == 1: LOG.debug("It is a service which I should build.") + datacenter_id = data['datacenter_id'] + dc = db_api.datacenter_get(context['conf'],data['tenant_id'], + data['datacenter_id']) + datacenter = db_api.unpack_extra(dc) + context['stack_name']=datacenter['name'] return True else: return False def plan_changes(self, context, event, data): + # Here we can plan multiple command execution. + # It might be Heat call command, then chef call command and other + # + LOG.debug("Plan changes...") + self.prepare_template(context, event, data) + self.chef_configuration(context, event, data) + context['commands'].append(self.deploy_template_command(context, event, data)) + context['commands'].append(self.chef_configuration_command(context, event, data)) pass + def prepare_template(self, context, event, data): + LOG.debug("Prepare CloudFormation Template...") + template = Template() + template.add_description('Base template for Active Directory deployment') + sec_grp = template.create_security_group('Security group for AD') + rule = template.create_securitygroup_rule('tcp','3389','3389','0.0.0.0/0') + template.add_rule_to_securitygroup(sec_grp, rule) + template.add_resource('ADSecurityGroup', sec_grp) + + instance = template.create_instance() + instance_name= 'AD-DC001' + template.add_security_group(instance, 'ADSecurityGroup') + template.add_resource(instance_name, instance) + + template.add_output_value(instance_name+'-IP',{"Fn::GetAtt" : [instance_name,'PublicIp']}, + 'Public IP for the domain controller.') + context['template']=template + pass + + def deploy_template_command(self, context, event, data): + LOG.debug("Creating CloudFormation Template deployment command...") + fname = "templates/"+str(uuid.uuid4()) + f=open(fname, "w") + f.write(context['template'].to_json()) + f.close() + context['template_name']=fname + command = command_api.Command(command_api.TEMPLATE_DEPLOYMENT_COMMAND, context) + return command + pass + + def chef_configuration(self, context, event, data): + LOG.debug("Creating Chef configuration...") + context['Role'] = 'pdc' + pass + + def chef_configuration_command(self, context, event, data): + LOG.debug("Creating Chef configuration command...") + command = command_api.Command(command_api.CHEF_COMMAND, context) + return command + + def submit_commands(self, context, event, data): + LOG.debug("Submit commands for execution...") + pass \ No newline at end of file diff --git a/windc/windc/core/change_events.py b/windc/windc/core/change_events.py index 8324a7f73..8955b58da 100644 --- a/windc/windc/core/change_events.py +++ b/windc/windc/core/change_events.py @@ -20,6 +20,8 @@ import logging LOG = logging.getLogger(__name__) from windc.core import builder_set +from windc.core import builder +from windc.drivers import command_executor #Declare events types SCOPE_SERVICE_CHANGE = "Service" @@ -40,11 +42,14 @@ class Event: def change_event(conf, event, data): LOG.info("Change event of type: %s ", event) - context = {} + context = builder.create_context() context['conf'] = conf for builder_type in builder_set.builders.set: - builder = builder_set.builders.set[builder_type] - builder.build(context, event, data) + builder_instance = builder_set.builders.set[builder_type] + builder_instance.build(context, event, data) + + executor = command_executor.Executor() + executor.execute(context['commands']) pass diff --git a/windc/windc/core/commands.py b/windc/windc/core/commands.py new file mode 100644 index 000000000..089ec2777 --- /dev/null +++ b/windc/windc/core/commands.py @@ -0,0 +1,33 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +TEMPLATE_DEPLOYMENT_COMMAND = "Template" +CHEF_COMMAND = "Chef" + +class Command: + type = "Empty" + context = None + + def __init__(self): + self.type = "Empty" + self.context = None + + def __init__(self, type, context): + self.type = type + self.context = context + + diff --git a/windc/windc/core/templates.py b/windc/windc/core/templates.py new file mode 100644 index 000000000..47a7c90d2 --- /dev/null +++ b/windc/windc/core/templates.py @@ -0,0 +1,107 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import logging +from windc.common.wsgi import JSONResponseSerializer +LOG = logging.getLogger(__name__) + +class Template: + def __init__(self): + self.content = {'AWSTemplateFormatVersion':'2010-09-09', 'Description':'', + 'Parameters':{}} + self.content['Mappings'] = { + "AWSInstanceType2Arch" : { + "t1.micro" : { "Arch" : "32" }, + "m1.small" : { "Arch" : "32" }, + "m1.large" : { "Arch" : "64" }, + "m1.xlarge" : { "Arch" : "64" }, + "m2.xlarge" : { "Arch" : "64" }, + "m2.2xlarge" : { "Arch" : "64" }, + "m2.4xlarge" : { "Arch" : "64" }, + "c1.medium" : { "Arch" : "32" }, + "c1.xlarge" : { "Arch" : "64" }, + "cc1.4xlarge" : { "Arch" : "64" } + }, + "DistroArch2AMI": { + "F16" : { "32" : "F16-i386-cfntools", "64" : "F16-x86_64-cfntools" }, + "F17" : { "32" : "F17-i386-cfntools", "64" : "F17-x86_64-cfntools" }, + "U10" : { "32" : "U10-i386-cfntools", "64" : "U10-x86_64-cfntools" }, + "RHEL-6.1": { "32" : "rhel61-i386-cfntools", "64" : "rhel61-x86_64-cfntools" }, + "RHEL-6.2": { "32" : "rhel62-i386-cfntools", "64" : "rhel62-x86_64-cfntools" }, + "RHEL-6.3": { "32" : "rhel63-i386-cfntools", "64" : "rhel63-x86_64-cfntools" } + } + } + self.content['Resources'] = {} + self.content['Outputs'] = {} + + def to_json(self): + serializer = JSONResponseSerializer() + json = serializer.to_json(self.content) + return json + + + def empty_template(self): + pass + + def add_description(self, description): + self.content['Description'] = description + + def add_parameter(self, name, parameter): + self.content['Parameters'].update({name : parameter}) + + def add_resource(self, name, resource): + self.content['Resources'].update({name : resource}) + + def create_parameter(self, defult, type, decription): + parameter = {'Default':default, 'Type':type, 'Description':description} + return parameter + + def create_security_group(self, description): + sec_grp = {'Type':'AWS::EC2::SecurityGroup'} + sec_grp['Properties'] = {} + sec_grp['Properties']['GroupDescription'] = description + sec_grp['Properties']['SecurityGroupIngress'] = [] + return sec_grp + + def add_rule_to_securitygroup(self, grp, rule): + grp['Properties']['SecurityGroupIngress'].append(rule) + + def create_securitygroup_rule(self, proto, f_port, t_port, cidr): + rule = {'IpProtocol':proto, 'FromPort':f_port, 'ToPort':t_port,'CidrIp': cidr} + return rule + + def create_instance(self): + instance = {'Type':'AWS::EC2::Instance','Metadata':{},'Properties':{}} + instance['Properties']['ImageId'] = 'U10-x86_64-cfntools' + instance['Properties']['SecurityGroups']=[] + instance['Properties']['KeyName'] = 'keero-linux-keys' + instance['Properties']['InstanceType'] = 'm1.small' + return instance + + def add_security_group(self, instance, grp_name): + instance['Properties']['SecurityGroups'].append({'Ref': grp_name}) + + def add_output_value(self, name, value, description): + self.content['Outputs'].update({name:{'Value':value, 'Description':description}}) + + def get_content(self): + return self.content + + + + diff --git a/windc/windc/drivers/command_executor.py b/windc/windc/drivers/command_executor.py new file mode 100644 index 000000000..c7c0d2f1e --- /dev/null +++ b/windc/windc/drivers/command_executor.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from windc.core import commands as commands_api +from windc.drivers import openstack_heat + +class Executor: + + map = {commands_api.TEMPLATE_DEPLOYMENT_COMMAND : openstack_heat.Heat} + + def __init__(self): + pass + + def execute(self, commands): + for command in commands: + if command.type == commands_api.TEMPLATE_DEPLOYMENT_COMMAND: + executor = openstack_heat.Heat() + executor.execute(command) + + diff --git a/windc/windc/drivers/openstack_heat.py b/windc/windc/drivers/openstack_heat.py new file mode 100644 index 000000000..7661bb683 --- /dev/null +++ b/windc/windc/drivers/openstack_heat.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +#from heatclient import Client +from subprocess import call + +import logging +LOG = logging.getLogger(__name__) + +class Heat: + + def __init__(self): + pass + + def execute(self, command): +# client = Client('1',OS_IMAGE_ENDPOINT, OS_TENANT_ID) + LOG.debug('Calling heat script to execute template') + call(["./heat_run","stack-create","-f "+command.context['template_name'], + command.context['stack_name']]) + pass +