6cdbb8eef1
* Cleaned up some of the code that gets the action settings. * Added the option of per task action settings just in case a given task needs to reuse an action with different settings. Change-Id: I8194cd3155ac3db3faaf0de8b87617f3891d0b10
317 lines
11 KiB
Python
317 lines
11 KiB
Python
# Copyright (C) 2015 Catalyst IT Ltd
|
|
#
|
|
# 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 stacktask.actions.v1.base import BaseAction, ProjectMixin
|
|
from django.conf import settings
|
|
from stacktask.actions import openstack_clients, user_store
|
|
import six
|
|
|
|
|
|
class NewDefaultNetworkAction(BaseAction, ProjectMixin):
|
|
"""
|
|
This action will setup all required basic networking
|
|
resources so that a new user can launch instances
|
|
right away.
|
|
"""
|
|
|
|
required = [
|
|
'setup_network',
|
|
'project_id',
|
|
'region',
|
|
]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(NewDefaultNetworkAction, self).__init__(*args, **kwargs)
|
|
|
|
def _validate_region(self):
|
|
if not self.region:
|
|
self.add_note('ERROR: No region given.')
|
|
return False
|
|
|
|
id_manager = user_store.IdentityManager()
|
|
region = id_manager.get_region(self.region)
|
|
if not region:
|
|
self.add_note('ERROR: Region does not exist.')
|
|
return False
|
|
|
|
self.add_note('Region: %s exists.' % self.region)
|
|
return True
|
|
|
|
def _validate_defaults(self):
|
|
defaults = self.settings.get(self.region, {})
|
|
|
|
if not defaults:
|
|
self.add_note('ERROR: No default settings for region %s.' %
|
|
self.region)
|
|
return False
|
|
return True
|
|
|
|
def _validate_keystone_user(self):
|
|
keystone_user = self.action.task.keystone_user
|
|
if keystone_user.get('project_id') != self.project_id:
|
|
self.add_note('Project id does not match keystone user project.')
|
|
return False
|
|
return True
|
|
|
|
def _validate(self):
|
|
self.action.valid = (
|
|
self._validate_region() and
|
|
self._validate_project_id() and
|
|
self._validate_defaults() and
|
|
self._validate_keystone_user()
|
|
)
|
|
self.action.save()
|
|
|
|
def _create_network(self):
|
|
neutron = openstack_clients.get_neutronclient(region=self.region)
|
|
defaults = self.settings.get(self.region, {})
|
|
|
|
if not self.get_cache('network_id'):
|
|
try:
|
|
network_body = {
|
|
"network": {
|
|
"name": defaults['network_name'],
|
|
'tenant_id': self.project_id,
|
|
"admin_state_up": True
|
|
}
|
|
}
|
|
network = neutron.create_network(body=network_body)
|
|
except Exception as e:
|
|
self.add_note(
|
|
"Error: '%s' while creating network: %s" %
|
|
(e, defaults['network_name']))
|
|
raise
|
|
self.set_cache('network_id', network['network']['id'])
|
|
self.add_note("Network %s created for project %s" %
|
|
(defaults['network_name'],
|
|
self.project_id))
|
|
else:
|
|
self.add_note("Network %s already created for project %s" %
|
|
(defaults['network_name'],
|
|
self.project_id))
|
|
|
|
if not self.get_cache('subnet_id'):
|
|
try:
|
|
subnet_body = {
|
|
"subnet": {
|
|
"network_id": self.get_cache('network_id'),
|
|
"ip_version": 4,
|
|
'tenant_id': self.project_id,
|
|
'dns_nameservers': defaults['DNS_NAMESERVERS'],
|
|
"cidr": defaults['SUBNET_CIDR']
|
|
}
|
|
}
|
|
subnet = neutron.create_subnet(body=subnet_body)
|
|
except Exception as e:
|
|
self.add_note(
|
|
"Error: '%s' while creating subnet" % e)
|
|
raise
|
|
self.set_cache('subnet_id', subnet['subnet']['id'])
|
|
self.add_note("Subnet created for network %s" %
|
|
defaults['network_name'])
|
|
else:
|
|
self.add_note("Subnet already created for network %s" %
|
|
defaults['network_name'])
|
|
|
|
if not self.get_cache('router_id'):
|
|
try:
|
|
router_body = {
|
|
"router": {
|
|
"name": defaults['router_name'],
|
|
"external_gateway_info": {
|
|
"network_id": defaults['public_network']
|
|
},
|
|
'tenant_id': self.project_id,
|
|
"admin_state_up": True
|
|
}
|
|
}
|
|
router = neutron.create_router(body=router_body)
|
|
except Exception as e:
|
|
self.add_note(
|
|
"Error: '%s' while creating router: %s" %
|
|
(e, defaults['router_name']))
|
|
raise
|
|
self.set_cache('router_id', router['router']['id'])
|
|
self.add_note("Router created for project %s" %
|
|
self.project_id)
|
|
else:
|
|
self.add_note("Router already created for project %s" %
|
|
self.project_id)
|
|
|
|
try:
|
|
interface_body = {
|
|
"subnet_id": self.get_cache('subnet_id')
|
|
}
|
|
neutron.add_interface_router(self.get_cache('router_id'),
|
|
body=interface_body)
|
|
except Exception as e:
|
|
self.add_note(
|
|
"Error: '%s' while attaching interface" % e)
|
|
raise
|
|
self.add_note("Interface added to router for subnet")
|
|
|
|
def _pre_approve(self):
|
|
# Note: Do we need to get this from cache? it is a required setting
|
|
# self.project_id = self.action.task.cache.get('project_id', None)
|
|
self._validate()
|
|
|
|
def _post_approve(self):
|
|
self._validate()
|
|
|
|
if self.setup_network and self.valid:
|
|
self._create_network()
|
|
|
|
def _submit(self, token_data):
|
|
pass
|
|
|
|
|
|
class NewProjectDefaultNetworkAction(NewDefaultNetworkAction):
|
|
"""
|
|
A variant of NewDefaultNetwork that expects the project
|
|
to not be created until after post_approve.
|
|
"""
|
|
|
|
required = [
|
|
'setup_network',
|
|
'region',
|
|
]
|
|
|
|
def _pre_validate(self):
|
|
# Note: Don't check project here as it doesn't exist yet.
|
|
self.action.valid = (
|
|
self._validate_region() and
|
|
self._validate_defaults()
|
|
)
|
|
self.action.save()
|
|
|
|
def _validate(self):
|
|
self.action.valid = (
|
|
self._validate_region() and
|
|
self._validate_project_id() and
|
|
self._validate_defaults()
|
|
)
|
|
self.action.save()
|
|
|
|
def _pre_approve(self):
|
|
self._pre_validate()
|
|
|
|
def _post_approve(self):
|
|
self.project_id = self.action.task.cache.get('project_id', None)
|
|
self._validate()
|
|
|
|
if self.setup_network and self.valid:
|
|
self._create_network()
|
|
|
|
|
|
class SetProjectQuotaAction(BaseAction):
|
|
""" Updates quota for a given project to a configured quota level """
|
|
|
|
class ServiceQuotaFunctor(object):
|
|
def __call__(self, project_id, values):
|
|
self.client.quotas.update(project_id, **values)
|
|
|
|
class ServiceQuotaCinderFunctor(ServiceQuotaFunctor):
|
|
def __init__(self, region_name):
|
|
self.client = openstack_clients.get_cinderclient(
|
|
region=region_name)
|
|
|
|
class ServiceQuotaNovaFunctor(ServiceQuotaFunctor):
|
|
def __init__(self, region_name):
|
|
self.client = openstack_clients.get_novaclient(
|
|
region=region_name)
|
|
|
|
class ServiceQuotaNeutronFunctor(ServiceQuotaFunctor):
|
|
def __init__(self, region_name):
|
|
self.client = openstack_clients.get_neutronclient(
|
|
region=region_name)
|
|
|
|
def __call__(self, project_id, values):
|
|
body = {
|
|
'quota': values
|
|
}
|
|
self.client.update_quota(project_id, body)
|
|
|
|
_quota_updaters = {
|
|
'cinder': ServiceQuotaCinderFunctor,
|
|
'nova': ServiceQuotaNovaFunctor,
|
|
'neutron': ServiceQuotaNeutronFunctor
|
|
}
|
|
|
|
def _validate_project_exists(self):
|
|
if not self.project_id:
|
|
self.add_note('No project_id set, previous action should have '
|
|
'set it.')
|
|
return False
|
|
|
|
id_manager = user_store.IdentityManager()
|
|
project = id_manager.get_project(self.project_id)
|
|
if not project:
|
|
self.add_note('Project with id %s does not exist.' %
|
|
self.project_id)
|
|
return False
|
|
self.add_note('Project with id %s exists.' % self.project_id)
|
|
return True
|
|
|
|
def _pre_validate(self):
|
|
# Nothing to validate yet.
|
|
self.action.valid = True
|
|
self.action.save()
|
|
|
|
def _validate(self):
|
|
# Make sure the project id is valid and can be used
|
|
self.action.valid = (
|
|
self._validate_project_exists()
|
|
)
|
|
self.action.save()
|
|
|
|
def _pre_approve(self):
|
|
self._pre_validate()
|
|
|
|
def _post_approve(self):
|
|
# Assumption: another action has placed the project_id into the cache.
|
|
self.project_id = self.action.task.cache.get('project_id', None)
|
|
self._validate()
|
|
|
|
if not self.valid or self.action.state == "completed":
|
|
return
|
|
|
|
# update quota for each openstack service
|
|
regions_dict = self.settings.get('regions', {})
|
|
for region_name, region_settings in six.iteritems(regions_dict):
|
|
quota_size = region_settings.get('quota_size')
|
|
quota_settings = settings.PROJECT_QUOTA_SIZES.get(quota_size, {})
|
|
if not quota_settings:
|
|
self.add_note(
|
|
"Project quota not defined for size '%s' in region %s." % (
|
|
quota_size, region_name))
|
|
continue
|
|
for service_name, values in six.iteritems(quota_settings):
|
|
updater_class = self._quota_updaters.get(service_name)
|
|
if not updater_class:
|
|
self.add_note("No quota updater found for %s. Ignoring" %
|
|
service_name)
|
|
continue
|
|
# functor for the service+region
|
|
service_functor = updater_class(region_name)
|
|
service_functor(self.project_id, values)
|
|
self.add_note(
|
|
"Project quota for region %s set to %s" % (
|
|
region_name, quota_size))
|
|
|
|
self.action.state = "completed"
|
|
self.action.save()
|
|
|
|
def _submit(self, token_data):
|
|
pass
|