Applied pep8 fixes
Closes-Bug: #1703514 Change-Id: I89c25bd9e678dc2f7cc27f952aa4833e26c90f4a
This commit is contained in:
parent
c030c3d645
commit
264939cfb5
@ -1,21 +1,19 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from apiclient.http import HttpMock
|
||||
from googleapiclient.discovery import build
|
||||
import os
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + '/data'
|
||||
|
||||
|
@ -1,26 +1,25 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import mock
|
||||
import os
|
||||
|
||||
from cinder import context
|
||||
from cinder import test
|
||||
from cinder.volume.drivers.gce.driver import GceDriver
|
||||
from cinder.tests.unit.volume.drivers.gce import gce_mock
|
||||
from cinder.tests.unit.fake_volume import fake_volume_obj
|
||||
from cinder.tests.unit.fake_snapshot import fake_snapshot_obj
|
||||
from cinder.tests.unit.fake_volume import fake_volume_obj
|
||||
from cinder.tests.unit.volume.drivers.gce import gce_mock
|
||||
from cinder.volume.drivers.gce.driver import GceDriver
|
||||
from cinder.volume.drivers.gce.gceutils import GceOperationError
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + '/data'
|
||||
|
@ -10,19 +10,20 @@ 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 mock
|
||||
|
||||
from oslo_service import loopingcall
|
||||
from cinder import context
|
||||
from cinder.exception import APITimeout
|
||||
from cinder.exception import NotFound
|
||||
from cinder.exception import VolumeNotFound
|
||||
from cinder import test
|
||||
from cinder.exception import APITimeout, NotFound, VolumeNotFound
|
||||
from cinder.volume.drivers.aws import ebs
|
||||
from cinder.volume.drivers.aws.exception import AvailabilityZoneNotFound
|
||||
import mock
|
||||
from moto import mock_ec2
|
||||
from oslo_service import loopingcall
|
||||
|
||||
|
||||
class EBSVolumeTestCase(test.TestCase):
|
||||
|
||||
@mock_ec2
|
||||
def setUp(self):
|
||||
super(EBSVolumeTestCase, self).setUp()
|
||||
@ -79,11 +80,13 @@ class EBSVolumeTestCase(test.TestCase):
|
||||
def wait(*args):
|
||||
def _wait():
|
||||
raise loopingcall.LoopingCallDone(False)
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait)
|
||||
return timer.start(interval=1).wait()
|
||||
|
||||
mock_wait.side_effect = wait
|
||||
self.assertRaises(APITimeout, self._driver.create_volume, self._stub_volume())
|
||||
self.assertRaises(APITimeout, self._driver.create_volume,
|
||||
self._stub_volume())
|
||||
|
||||
@mock_ec2
|
||||
def test_volume_deletion(self):
|
||||
@ -121,6 +124,7 @@ class EBSVolumeTestCase(test.TestCase):
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait)
|
||||
return timer.start(interval=1).wait()
|
||||
|
||||
mock_wait.side_effect = wait
|
||||
ss = self._stub_snapshot()
|
||||
self._driver.create_volume(ss['volume'])
|
||||
@ -132,7 +136,8 @@ class EBSVolumeTestCase(test.TestCase):
|
||||
volume = self._stub_volume()
|
||||
self._driver.create_volume(volume)
|
||||
self._driver.create_snapshot(snapshot)
|
||||
self.assertIsNone(self._driver.create_volume_from_snapshot(volume, snapshot))
|
||||
self.assertIsNone(
|
||||
self._driver.create_volume_from_snapshot(volume, snapshot))
|
||||
|
||||
@mock_ec2
|
||||
def test_volume_from_non_existing_snapshot(self):
|
||||
|
@ -10,34 +10,39 @@ 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 time
|
||||
|
||||
from boto import ec2
|
||||
from boto.regioninfo import RegionInfo
|
||||
from oslo_service import loopingcall
|
||||
from oslo_log import log as logging
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder.exception import VolumeNotFound, NotFound, APITimeout, InvalidConfigurationValue
|
||||
from cinder.exception import APITimeout
|
||||
from cinder.exception import InvalidConfigurationValue
|
||||
from cinder.exception import NotFound
|
||||
from cinder.exception import VolumeNotFound
|
||||
from cinder.volume.driver import BaseVD
|
||||
from cinder.volume.drivers.aws.exception import AvailabilityZoneNotFound
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
|
||||
from exception import AvailabilityZoneNotFound
|
||||
|
||||
aws_group = cfg.OptGroup(name='AWS', title='Options to connect to an AWS environment')
|
||||
aws_group = cfg.OptGroup(name='AWS',
|
||||
title='Options to connect to an AWS environment')
|
||||
aws_opts = [
|
||||
cfg.StrOpt('secret_key', help='Secret key of AWS account', secret=True),
|
||||
cfg.StrOpt('access_key', help='Access key of AWS account', secret=True),
|
||||
cfg.StrOpt('region_name', help='AWS region'),
|
||||
cfg.StrOpt('az', help='AWS availability zone'),
|
||||
cfg.IntOpt('wait_time_min', help='Maximum wait time for AWS operations', default=5)
|
||||
cfg.IntOpt('wait_time_min', help='Maximum wait time for AWS operations',
|
||||
default=5)
|
||||
]
|
||||
|
||||
ebs_opts = [
|
||||
cfg.StrOpt('ebs_pool_name', help='Storage pool name'),
|
||||
cfg.IntOpt('ebs_free_capacity_gb', help='Free space available on EBS storage pool',
|
||||
default=1024),
|
||||
cfg.IntOpt('ebs_total_capacity_gb', help='Total space available on EBS storage pool',
|
||||
default=1024)
|
||||
cfg.IntOpt('ebs_free_capacity_gb',
|
||||
help='Free space available on EBS storage pool', default=1024),
|
||||
cfg.IntOpt('ebs_total_capacity_gb',
|
||||
help='Total space available on EBS storage pool', default=1024)
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -48,9 +53,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EBSDriver(BaseVD):
|
||||
"""
|
||||
Implements cinder volume interface with EBS as storage backend.
|
||||
"""
|
||||
"""Implements cinder volume interface with EBS as storage backend."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EBSDriver, self).__init__(*args, **kwargs)
|
||||
self.VERSION = '1.0.0'
|
||||
@ -70,10 +73,11 @@ class EBSDriver(BaseVD):
|
||||
region_name = CONF.AWS.region_name
|
||||
endpoint = '.'.join(['ec2', region_name, 'amazonaws.com'])
|
||||
region = RegionInfo(name=region_name, endpoint=endpoint)
|
||||
self._conn = ec2.EC2Connection(aws_access_key_id=CONF.AWS.access_key,
|
||||
self._conn = ec2.EC2Connection(
|
||||
aws_access_key_id=CONF.AWS.access_key,
|
||||
aws_secret_access_key=CONF.AWS.secret_key,
|
||||
region=region)
|
||||
# resort to first AZ for now. TODO: expose this through API
|
||||
# resort to first AZ for now. TODO(do_setup): expose this through API
|
||||
az = CONF.AWS.az
|
||||
|
||||
try:
|
||||
@ -84,7 +88,6 @@ class EBSDriver(BaseVD):
|
||||
|
||||
self.set_initialized()
|
||||
|
||||
|
||||
def _wait_for_create(self, id, final_state):
|
||||
def _wait_for_status(start_time):
|
||||
current_time = time.time()
|
||||
@ -96,7 +99,8 @@ class EBSDriver(BaseVD):
|
||||
if obj.status == final_state:
|
||||
raise loopingcall.LoopingCallDone(True)
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_status, time.time())
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_status,
|
||||
time.time())
|
||||
return timer.start(interval=5).wait()
|
||||
|
||||
def _wait_for_snapshot(self, id, final_state):
|
||||
@ -109,7 +113,8 @@ class EBSDriver(BaseVD):
|
||||
if obj.status == final_state:
|
||||
raise loopingcall.LoopingCallDone(True)
|
||||
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_status, time.time())
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_status,
|
||||
time.time())
|
||||
return timer.start(interval=5).wait()
|
||||
|
||||
def create_volume(self, volume):
|
||||
@ -117,7 +122,8 @@ class EBSDriver(BaseVD):
|
||||
ebs_vol = self._conn.create_volume(size, self._zone)
|
||||
if self._wait_for_create(ebs_vol.id, 'available') is False:
|
||||
raise APITimeout(service='EC2')
|
||||
self._conn.create_tags([ebs_vol.id], {'project_id': volume['project_id'],
|
||||
self._conn.create_tags([ebs_vol.id],
|
||||
{'project_id': volume['project_id'],
|
||||
'uuid': volume['id'],
|
||||
'is_clone': False,
|
||||
'created_at': volume['created_at'],
|
||||
@ -139,7 +145,7 @@ class EBSDriver(BaseVD):
|
||||
self._conn.delete_volume(ebs_vol.id)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
# TODO throw errors if AWS config is broken
|
||||
# TODO(check_setup_error) throw errors if AWS config is broken
|
||||
pass
|
||||
|
||||
def create_export(self, context, volume, connector):
|
||||
@ -198,7 +204,8 @@ class EBSDriver(BaseVD):
|
||||
if self._wait_for_snapshot(ebs_snap.id, 'completed') is False:
|
||||
raise APITimeout(service='EC2')
|
||||
|
||||
self._conn.create_tags([ebs_snap.id], {'project_id': snapshot['project_id'],
|
||||
self._conn.create_tags([ebs_snap.id],
|
||||
{'project_id': snapshot['project_id'],
|
||||
'uuid': snapshot['id'],
|
||||
'is_clone': True,
|
||||
'created_at': snapshot['created_at'],
|
||||
@ -222,7 +229,8 @@ class EBSDriver(BaseVD):
|
||||
|
||||
if self._wait_for_create(ebs_vol.id, 'available') is False:
|
||||
raise APITimeout(service='EC2')
|
||||
self._conn.create_tags([ebs_vol.id], {'project_id': volume['project_id'],
|
||||
self._conn.create_tags([ebs_vol.id],
|
||||
{'project_id': volume['project_id'],
|
||||
'uuid': volume['id'],
|
||||
'is_clone': False,
|
||||
'created_at': volume['created_at'],
|
||||
@ -239,7 +247,3 @@ class EBSDriver(BaseVD):
|
||||
|
||||
def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
|
||||
raise NotImplemented()
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -15,5 +15,6 @@ limitations under the License.
|
||||
from cinder.exception import CinderException
|
||||
from cinder.i18n import _
|
||||
|
||||
|
||||
class AvailabilityZoneNotFound(CinderException):
|
||||
message = _("Availability Zone %(az)s was not found")
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
@ -71,6 +70,7 @@ if not hasattr(loopingcall, 'FixedIntervalWithTimeoutLoopingCall'):
|
||||
|
||||
def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
"""Wait for GCE operation to complete, raise error if operation failure
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -108,6 +108,7 @@ def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
|
||||
def get_gce_service(service_key):
|
||||
"""Returns GCE compute resource object for interacting with GCE API
|
||||
|
||||
:param service_key: string, Path of service key obtained from
|
||||
https://console.cloud.google.com/apis/credentials
|
||||
:return: :class:`Resource <Resource>` object
|
||||
@ -120,6 +121,7 @@ def get_gce_service(service_key):
|
||||
|
||||
def create_disk(compute, project, zone, name, size):
|
||||
"""Create disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -140,6 +142,7 @@ def create_disk(compute, project, zone, name, size):
|
||||
|
||||
def delete_disk(compute, project, zone, name):
|
||||
"""Delete disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -153,6 +156,7 @@ def delete_disk(compute, project, zone, name):
|
||||
|
||||
def get_disk(compute, project, zone, name):
|
||||
"""Get info of disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -165,6 +169,7 @@ def get_disk(compute, project, zone, name):
|
||||
|
||||
def snapshot_disk(compute, project, zone, name, snapshot_name):
|
||||
"""Create snapshot of disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -180,6 +185,7 @@ def snapshot_disk(compute, project, zone, name, snapshot_name):
|
||||
|
||||
def get_snapshot(compute, project, name):
|
||||
"""Get info of snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE snapshot name
|
||||
@ -191,6 +197,7 @@ def get_snapshot(compute, project, name):
|
||||
|
||||
def delete_snapshot(compute, project, name):
|
||||
"""Delete snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE snapshot name
|
||||
@ -202,6 +209,7 @@ def delete_snapshot(compute, project, name):
|
||||
|
||||
def create_disk_from_snapshot(compute, project, zone, name, snapshot_name):
|
||||
"""Create disk from snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
|
@ -1,24 +1,17 @@
|
||||
# Copyright (c) 2016 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
|
||||
'''
|
||||
1. Source your Openstack RC file.
|
||||
2. Run this script as: python create-glance-images-aws.py <access-key> <secret-key> <region-name>
|
||||
'''
|
||||
"""
|
||||
Copyright (c) 2016 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
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 boto3
|
||||
import ConfigParser
|
||||
import hashlib
|
||||
import keystoneauth1
|
||||
import os
|
||||
@ -26,15 +19,17 @@ import requests
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1.identity import v3
|
||||
from keystoneauth1 import session
|
||||
|
||||
|
||||
class AwsImages(object):
|
||||
|
||||
def __init__(self, credentials):
|
||||
self.ec2_client = boto3.client('ec2', **credentials)
|
||||
self.glance_client = RestClient()
|
||||
self.aws_image_types = {'machine': 'ami', 'kernel': 'aki', 'ramdisk': 'ari'}
|
||||
self.aws_image_types = {'machine': 'ami', 'kernel': 'aki',
|
||||
'ramdisk': 'ari'}
|
||||
|
||||
def register_aws_images(self):
|
||||
response = self.ec2_client.describe_images(Owners=['self'])
|
||||
@ -44,8 +39,8 @@ class AwsImages(object):
|
||||
self.create_image(self._aws_to_ostack_formatter(img))
|
||||
|
||||
def create_image(self, img_data):
|
||||
"""
|
||||
Create an OpenStack image.
|
||||
"""Create an OpenStack image.
|
||||
|
||||
:param img_data: dict -- Describes AWS AMI
|
||||
:returns: dict -- Response from REST call
|
||||
:raises: requests.HTTPError
|
||||
@ -58,10 +53,11 @@ class AwsImages(object):
|
||||
'metadata': {'ami_id': ami_id}}]
|
||||
}
|
||||
try:
|
||||
resp = self.glance_client.request('POST', '/v2/images', json=img_data)
|
||||
resp = self.glance_client.request('POST', '/v2/images',
|
||||
json=img_data)
|
||||
resp.raise_for_status()
|
||||
# Need to update the image in the registry with location information so
|
||||
# the status changes from 'queued' to 'active'
|
||||
# Need to update the image in the registry with location
|
||||
# information so the status changes from 'queued' to 'active'
|
||||
self.update_properties(glance_id, img_props)
|
||||
except keystoneauth1.exceptions.http.Conflict as e:
|
||||
# ignore error if image already exists
|
||||
@ -70,8 +66,8 @@ class AwsImages(object):
|
||||
raise e
|
||||
|
||||
def update_properties(self, imageid, props):
|
||||
"""
|
||||
Add or update a set of image properties on an image.
|
||||
"""Add or update a set of image properties on an image.
|
||||
|
||||
:param imageid: int -- The Ostack image UUID
|
||||
:param props: dict -- Image properties to update
|
||||
"""
|
||||
@ -84,7 +80,8 @@ class AwsImages(object):
|
||||
'path': '/%s' % name,
|
||||
'value': value
|
||||
})
|
||||
resp = self.glance_client.request('PATCH', '/v2/images/%s' % imageid, json=patch_body)
|
||||
resp = self.glance_client.request('PATCH', '/v2/images/%s' % imageid,
|
||||
json=patch_body)
|
||||
resp.raise_for_status()
|
||||
|
||||
def _get_image_uuid(self, ami_id):
|
||||
@ -93,8 +90,8 @@ class AwsImages(object):
|
||||
return str(uuid.UUID(bytes=md.digest()))
|
||||
|
||||
def _aws_to_ostack_formatter(self, aws_obj):
|
||||
"""
|
||||
Converts aws img data to Openstack img data format.
|
||||
"""Converts aws img data to Openstack img data format.
|
||||
|
||||
:param img(dict): aws img data
|
||||
:return(dict): ostack img data
|
||||
"""
|
||||
@ -105,14 +102,17 @@ class AwsImages(object):
|
||||
for bdm in aws_obj.get('BlockDeviceMappings'):
|
||||
if 'Ebs' in bdm:
|
||||
ebs_vol_sizes.append(bdm['Ebs']['VolumeSize'])
|
||||
elif 'VirtualName' in bdm and bdm['VirtualName'].startswith('ephemeral'):
|
||||
elif 'VirtualName' in bdm and bdm['VirtualName'].startswith(
|
||||
'ephemeral'):
|
||||
# for instance-store volumes, size is not available
|
||||
num_istore_vols += 1
|
||||
if aws_obj.get('RootDeviceType' == 'instance-store') and num_istore_vols == 0:
|
||||
if (aws_obj.get('RootDeviceType' == 'instance-store') and
|
||||
num_istore_vols == 0):
|
||||
# list of bdms can be empty for instance-store volumes
|
||||
num_istore_vols = 1
|
||||
# generate glance image uuid based on AWS image id
|
||||
image_id = self._get_image_uuid(aws_obj.get('ImageId'))
|
||||
description = aws_obj.get('Description') or 'Discovered image'
|
||||
|
||||
return {
|
||||
'id': image_id,
|
||||
@ -120,7 +120,7 @@ class AwsImages(object):
|
||||
'container_format': self.aws_image_types[aws_obj.get('ImageType')],
|
||||
'disk_format': self.aws_image_types[aws_obj.get('ImageType')],
|
||||
'visibility': visibility,
|
||||
'pf9_description' : aws_obj.get('Description') or 'Discovered image',
|
||||
'pf9_description': description,
|
||||
'aws_image_id': aws_obj.get('ImageId'),
|
||||
'aws_root_device_type': aws_obj.get('RootDeviceType'),
|
||||
'aws_ebs_vol_sizes': str(ebs_vol_sizes),
|
||||
@ -128,7 +128,6 @@ class AwsImages(object):
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RestClient(object):
|
||||
def __init__(self):
|
||||
os_auth_url = os.getenv('OS_AUTH_URL')
|
||||
@ -139,24 +138,24 @@ class RestClient(object):
|
||||
os_username = os.getenv('OS_USERNAME')
|
||||
os_password = os.getenv('OS_PASSWORD')
|
||||
os_tenant_name = os.getenv('OS_TENANT_NAME')
|
||||
os_region_name = os.getenv('OS_REGION_NAME')
|
||||
|
||||
self.glance_endpoint = os_auth_url.replace('keystone/v3', 'glance')
|
||||
sys.stdout.write('Using glance endpoint: ' + self.glance_endpoint)
|
||||
|
||||
v3_auth = v3.Password(auth_url=os_auth_url, username=os_username,
|
||||
password = os_password, project_name = os_tenant_name,
|
||||
project_domain_name = 'default', user_domain_name = 'default')
|
||||
password=os_password,
|
||||
project_name=os_tenant_name,
|
||||
project_domain_name='default',
|
||||
user_domain_name='default')
|
||||
self.sess = session.Session(auth=v3_auth, verify=False) # verify=True
|
||||
|
||||
def request(self, method, path, **kwargs):
|
||||
"""
|
||||
Make a requests request with retry/relogin on auth failure.
|
||||
"""
|
||||
"""Make a requests request with retry/relogin on auth failure."""
|
||||
url = self.glance_endpoint + path
|
||||
headers = self.sess.get_auth_headers()
|
||||
if method == 'PUT' or method == 'PATCH':
|
||||
headers['Content-Type'] = 'application/openstack-images-v2.1-json-patch'
|
||||
content_type = 'application/openstack-images-v2.1-json-patch'
|
||||
headers['Content-Type'] = content_type
|
||||
resp = requests.request(method, url, headers=headers, **kwargs)
|
||||
else:
|
||||
resp = self.sess.request(url, method, headers=headers, **kwargs)
|
||||
@ -164,11 +163,12 @@ class RestClient(object):
|
||||
return resp
|
||||
|
||||
|
||||
### MAIN ###
|
||||
# MAIN
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 4:
|
||||
sys.stderr.write('Incorrect usage: this script takes exactly 3 arguments.\n')
|
||||
sys.stderr.write('Incorrect usage: this script takes exactly 3 '
|
||||
'arguments.\n')
|
||||
sys.exit(1)
|
||||
|
||||
credentials = {}
|
||||
@ -178,4 +178,3 @@ if __name__ == '__main__':
|
||||
|
||||
aws_images = AwsImages(credentials)
|
||||
aws_images.register_aws_images()
|
||||
|
||||
|
@ -1,30 +1,26 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
'''
|
||||
1. Export Openstack RC file
|
||||
2. Run this script as: python create-glance-credentials.py <service-key-path>
|
||||
'''
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
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 gceutils
|
||||
import hashlib
|
||||
import keystoneauth1
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
import uuid
|
||||
import keystoneauth1
|
||||
import gceutils
|
||||
|
||||
from keystoneauth1 import loading, session
|
||||
from keystoneauth1 import loading
|
||||
from keystoneauth1 import session
|
||||
from keystoneclient import client
|
||||
from six.moves import urllib
|
||||
|
||||
|
@ -1,21 +1,19 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from googleapiclient.discovery import build
|
||||
from oauth2client.client import GoogleCredentials
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -1,30 +1,25 @@
|
||||
# Copyright (c) 2016 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright (c) 2016 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
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
|
||||
import socket
|
||||
|
||||
from six.moves import http_client
|
||||
from six.moves import urllib
|
||||
from oslo_config import cfg
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
from glance_store import capabilities
|
||||
import glance_store.driver
|
||||
from glance_store import exceptions
|
||||
from glance_store.i18n import _
|
||||
import glance_store.driver
|
||||
import glance_store.location
|
||||
from oslo_config import cfg
|
||||
from six.moves import urllib
|
||||
|
||||
import boto3
|
||||
import botocore.exceptions
|
||||
@ -35,11 +30,10 @@ MAX_REDIRECTS = 5
|
||||
STORE_SCHEME = 'aws'
|
||||
|
||||
aws_opts_group = cfg.OptGroup(name='aws', title='AWS specific options')
|
||||
aws_opts = [
|
||||
cfg.StrOpt('access_key', help='AWS access key ID'),
|
||||
aws_opts = [cfg.StrOpt('access_key', help='AWS access key ID'),
|
||||
cfg.StrOpt('secret_key', help='AWS secret access key'),
|
||||
cfg.StrOpt('region_name', help='AWS region name'),
|
||||
]
|
||||
cfg.StrOpt('region_name', help='AWS region name')]
|
||||
|
||||
|
||||
class StoreLocation(glance_store.location.StoreLocation):
|
||||
|
||||
@ -103,7 +97,6 @@ class Store(glance_store.driver.Store):
|
||||
self.__ec2_resource = boto3.resource('ec2', **self.credentials)
|
||||
return self.__ec2_resource
|
||||
|
||||
|
||||
@capabilities.check
|
||||
def get(self, location, offset=0, chunk_size=None, context=None):
|
||||
"""
|
||||
@ -133,7 +126,6 @@ class Store(glance_store.driver.Store):
|
||||
LOG.warn('**** ID of ami being deleted: {}'.format(ami_id))
|
||||
aws_client.deregister_image(ImageId=ami_id)
|
||||
|
||||
|
||||
def get_schemes(self):
|
||||
"""
|
||||
:retval tuple: containing valid scheme names to
|
||||
@ -141,7 +133,6 @@ class Store(glance_store.driver.Store):
|
||||
"""
|
||||
return ('aws',)
|
||||
|
||||
|
||||
def get_size(self, location, context=None):
|
||||
"""
|
||||
Takes a `glance_store.location.Location` object that indicates
|
||||
@ -169,5 +160,6 @@ class Store(glance_store.driver.Store):
|
||||
if ce.response['Error']['Code'] == 'InvalidAMIID.NotFound':
|
||||
raise exceptions.ImageDataNotFound()
|
||||
else:
|
||||
raise exceptions.GlanceStoreException(ce.response['Error']['Code'])
|
||||
raise exceptions.GlanceStoreException(
|
||||
ce.response['Error']['Code'])
|
||||
return size
|
||||
|
@ -1,23 +1,23 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
#
|
||||
# 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
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
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 gceutils
|
||||
import glance_store.driver
|
||||
import glance_store.location
|
||||
from glance_store import capabilities, exceptions
|
||||
import logging
|
||||
|
||||
from glance_store import capabilities
|
||||
from glance_store import exceptions
|
||||
from glance_store.i18n import _
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import units
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
@ -1,27 +1,26 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import mock
|
||||
import os
|
||||
|
||||
from glance_store._drivers.gce import Store
|
||||
from glance_store._drivers.gce import StoreLocation
|
||||
from glance_store import exceptions
|
||||
from glance_store import location
|
||||
from glance_store.location import Location
|
||||
from glance_store._drivers.gce import Store
|
||||
from glance_store._drivers.gce import StoreLocation
|
||||
from glance_store.tests.unit.gce import gce_mock
|
||||
from glance_store.tests import base
|
||||
from glance_store.tests.unit.gce import gce_mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import units
|
||||
|
||||
|
@ -1,34 +1,36 @@
|
||||
# Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 time
|
||||
|
||||
from ConfigParser import ConfigParser
|
||||
from neutron.common import exceptions
|
||||
from novaclient.v2 import client as novaclient
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
|
||||
import boto3
|
||||
import botocore
|
||||
import time
|
||||
|
||||
aws_group = cfg.OptGroup(name='AWS', title='Options to connect to an AWS environment')
|
||||
aws_group = cfg.OptGroup(name='AWS',
|
||||
title='Options to connect to an AWS environment')
|
||||
aws_opts = [
|
||||
cfg.StrOpt('secret_key', help='Secret key of AWS account', secret=True),
|
||||
cfg.StrOpt('access_key', help='Access key of AWS account', secret=True),
|
||||
cfg.StrOpt('region_name', help='AWS region'),
|
||||
cfg.StrOpt('az', help='AWS availability zone'),
|
||||
cfg.IntOpt('wait_time_min', help='Maximum wait time for AWS operations', default=5)
|
||||
cfg.IntOpt('wait_time_min', help='Maximum wait time for AWS operations',
|
||||
default=5)
|
||||
]
|
||||
|
||||
cfg.CONF.register_group(aws_group)
|
||||
@ -36,11 +38,13 @@ cfg.CONF.register_opts(aws_opts, group=aws_group)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _process_exception(e, dry_run):
|
||||
if dry_run:
|
||||
error_code = e.response['Code']
|
||||
if not error_code == 'DryRunOperation':
|
||||
raise exceptions.AwsException(error_code='AuthFailure',
|
||||
raise exceptions.AwsException(
|
||||
error_code='AuthFailure',
|
||||
message='Check your AWS authorization')
|
||||
else:
|
||||
if isinstance(e, botocore.exceptions.ClientError):
|
||||
@ -53,12 +57,13 @@ def _process_exception(e, dry_run):
|
||||
# Instead just propagate it up.
|
||||
raise e
|
||||
else:
|
||||
# TODO: This might display all Exceptions to the user which
|
||||
# might be irrelevant, keeping it until it becomes stable
|
||||
# TODO(exceptions): This might display all Exceptions to the user
|
||||
# which might be irrelevant, keeping it until it becomes stable
|
||||
error_message = e.msg
|
||||
raise exceptions.AwsException(error_code="NeutronError",
|
||||
message=error_message)
|
||||
|
||||
|
||||
def aws_exception(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
@ -68,7 +73,7 @@ def aws_exception(fn):
|
||||
return wrapper
|
||||
|
||||
|
||||
class AwsUtils:
|
||||
class AwsUtils(object):
|
||||
|
||||
def __init__(self):
|
||||
self.__ec2_client = None
|
||||
@ -83,20 +88,24 @@ class AwsUtils:
|
||||
|
||||
def get_nova_client(self):
|
||||
if self._nova_client is None:
|
||||
self._nova_client = novaclient.Client(username=cfg.CONF.nova_admin_username,
|
||||
api_key=cfg.CONF.nova_admin_password, auth_url=cfg.CONF.nova_admin_auth_url,
|
||||
self._nova_client = novaclient.Client(
|
||||
username=cfg.CONF.nova_admin_username,
|
||||
api_key=cfg.CONF.nova_admin_password,
|
||||
auth_url=cfg.CONF.nova_admin_auth_url,
|
||||
tenant_id=cfg.CONF.nova_admin_tenant_id,
|
||||
region_name=cfg.CONF.nova_region_name, insecure=True)
|
||||
return self._nova_client
|
||||
|
||||
def _get_ec2_client(self):
|
||||
if self.__ec2_client is None:
|
||||
self.__ec2_client = boto3.client('ec2', **self._neutron_credentials)
|
||||
self.__ec2_client = boto3.client('ec2',
|
||||
**self._neutron_credentials)
|
||||
return self.__ec2_client
|
||||
|
||||
def _get_ec2_resource(self):
|
||||
if self.__ec2_resource is None:
|
||||
self.__ec2_resource = boto3.resource('ec2', **self._neutron_credentials)
|
||||
self.__ec2_resource = boto3.resource('ec2',
|
||||
**self._neutron_credentials)
|
||||
return self.__ec2_resource
|
||||
|
||||
# Internet Gateway Operations
|
||||
@ -117,7 +126,8 @@ class AwsUtils:
|
||||
return internet_gateway['InternetGatewayId']
|
||||
|
||||
@aws_exception
|
||||
def create_tags_internet_gw_from_router_id(self, router_id, tags_list, dry_run=False):
|
||||
def create_tags_internet_gw_from_router_id(self, router_id, tags_list,
|
||||
dry_run=False):
|
||||
ig_id = self.get_internet_gw_from_router_id(router_id, dry_run)
|
||||
internet_gw_res = self._get_ec2_resource().InternetGateway(ig_id)
|
||||
internet_gw_res.create_tags(Tags=tags_list)
|
||||
@ -156,7 +166,8 @@ class AwsUtils:
|
||||
|
||||
@aws_exception
|
||||
def create_internet_gateway_resource(self, dry_run=False):
|
||||
internet_gw = self._get_ec2_client().create_internet_gateway(DryRun=dry_run)
|
||||
internet_gw = self._get_ec2_client().create_internet_gateway(
|
||||
DryRun=dry_run)
|
||||
ig_id = internet_gw['InternetGateway']['InternetGatewayId']
|
||||
return self._get_ec2_resource().InternetGateway(ig_id)
|
||||
|
||||
@ -169,9 +180,11 @@ class AwsUtils:
|
||||
return eip_addresses['Addresses']
|
||||
|
||||
@aws_exception
|
||||
def associate_elastic_ip_to_ec2_instance(self, elastic_ip, ec2_instance_id, dry_run=False):
|
||||
def associate_elastic_ip_to_ec2_instance(self, elastic_ip, ec2_instance_id,
|
||||
dry_run=False):
|
||||
allocation_id = None
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip, dry_run)
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip,
|
||||
dry_run)
|
||||
if len(eid_addresses) > 0:
|
||||
if 'AllocationId' in eid_addresses[0]:
|
||||
allocation_id = eid_addresses[0]['AllocationId']
|
||||
@ -193,9 +206,11 @@ class AwsUtils:
|
||||
return response
|
||||
|
||||
@aws_exception
|
||||
def disassociate_elastic_ip_from_ec2_instance(self, elastic_ip, dry_run=False):
|
||||
def disassociate_elastic_ip_from_ec2_instance(self, elastic_ip,
|
||||
dry_run=False):
|
||||
association_id = None
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip, dry_run)
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip,
|
||||
dry_run)
|
||||
if len(eid_addresses) > 0:
|
||||
if 'AssociationId' in eid_addresses[0]:
|
||||
association_id = eid_addresses[0]['AssociationId']
|
||||
@ -209,7 +224,8 @@ class AwsUtils:
|
||||
|
||||
@aws_exception
|
||||
def delete_elastic_ip(self, elastic_ip, dry_run=False):
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip, dry_run)
|
||||
eid_addresses = self.get_elastic_addresses_by_elastic_ip(elastic_ip,
|
||||
dry_run)
|
||||
if len(eid_addresses) > 0:
|
||||
if 'AllocationId' in eid_addresses[0]:
|
||||
allocation_id = eid_addresses[0]['AllocationId']
|
||||
@ -222,7 +238,8 @@ class AwsUtils:
|
||||
|
||||
# VPC Operations
|
||||
@aws_exception
|
||||
def get_vpc_from_neutron_network_id(self, neutron_network_id, dry_run=False):
|
||||
def get_vpc_from_neutron_network_id(self, neutron_network_id,
|
||||
dry_run=False):
|
||||
response = self._get_ec2_client().describe_vpcs(
|
||||
DryRun=dry_run,
|
||||
Filters=[
|
||||
@ -245,9 +262,7 @@ class AwsUtils:
|
||||
CidrBlock=cidr)['Vpc']['VpcId']
|
||||
vpc = self._get_ec2_resource().Vpc(vpc_id)
|
||||
waiter = self._get_ec2_client().get_waiter('vpc_available')
|
||||
waiter.wait(
|
||||
DryRun=dry_run,
|
||||
VpcIds = [ vpc_id ])
|
||||
waiter.wait(DryRun=dry_run, VpcIds=[vpc_id])
|
||||
vpc.create_tags(Tags=tags_list)
|
||||
return vpc_id
|
||||
|
||||
@ -292,7 +307,8 @@ class AwsUtils:
|
||||
)
|
||||
|
||||
@aws_exception
|
||||
def get_subnet_from_neutron_subnet_id(self, neutron_subnet_id, dry_run=False):
|
||||
def get_subnet_from_neutron_subnet_id(self, neutron_subnet_id,
|
||||
dry_run=False):
|
||||
response = self._get_ec2_client().describe_subnets(
|
||||
DryRun=dry_run,
|
||||
Filters=[
|
||||
@ -336,7 +352,8 @@ class AwsUtils:
|
||||
return response['RouteTables']
|
||||
|
||||
# Has ignore_errors special case so can't use decorator
|
||||
def create_default_route_to_ig(self, route_table_id, ig_id, dry_run=False, ignore_errors=False):
|
||||
def create_default_route_to_ig(self, route_table_id, ig_id, dry_run=False,
|
||||
ignore_errors=False):
|
||||
try:
|
||||
self._get_ec2_client().create_route(
|
||||
DryRun=dry_run,
|
||||
@ -345,12 +362,14 @@ class AwsUtils:
|
||||
GatewayId=ig_id,
|
||||
)
|
||||
except Exception as e:
|
||||
LOG.warning("Ignoring failure in creating default route to IG: %s" % e)
|
||||
LOG.warning("Ignoring failure in creating default route to IG: "
|
||||
"%s" % e)
|
||||
if not ignore_errors:
|
||||
_process_exception(e, dry_run)
|
||||
|
||||
# Has ignore_errors special case so can't use decorator
|
||||
def delete_default_route_to_ig(self, route_table_id, dry_run=False, ignore_errors=False):
|
||||
def delete_default_route_to_ig(self, route_table_id, dry_run=False,
|
||||
ignore_errors=False):
|
||||
try:
|
||||
self._get_ec2_client().delete_route(
|
||||
DryRun=dry_run,
|
||||
@ -361,7 +380,8 @@ class AwsUtils:
|
||||
if not ignore_errors:
|
||||
_process_exception(e, dry_run)
|
||||
else:
|
||||
LOG.warning("Ignoring failure in deleting default route to IG: %s" % e)
|
||||
LOG.warning("Ignoring failure in deleting default route to IG:"
|
||||
" %s" % e)
|
||||
|
||||
# Security group
|
||||
def _create_sec_grp_tags(self, secgrp, tags):
|
||||
@ -372,12 +392,13 @@ class AwsUtils:
|
||||
try:
|
||||
secgrp.reload()
|
||||
secgrp.create_tags(Tags=tags)
|
||||
except Exception as ex:
|
||||
except Exception:
|
||||
LOG.exception('Exception when adding tags to security groups.'
|
||||
' Retrying.')
|
||||
return
|
||||
raise loopingcall.LoopingCallDone(True)
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_state, time.time())
|
||||
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_state,
|
||||
time.time())
|
||||
return timer.start(interval=5).wait()
|
||||
|
||||
def _convert_openstack_rules_to_vpc(self, rules):
|
||||
@ -443,6 +464,7 @@ class AwsUtils:
|
||||
|
||||
def _create_sec_grp_rules(self, secgrp, rules):
|
||||
ingress, egress = self._convert_openstack_rules_to_vpc(rules)
|
||||
|
||||
def _wait_for_state(start_time):
|
||||
current_time = time.time()
|
||||
|
||||
@ -450,7 +472,7 @@ class AwsUtils:
|
||||
raise loopingcall.LoopingCallDone(False)
|
||||
try:
|
||||
self._refresh_sec_grp_rules(secgrp, ingress, egress)
|
||||
except Exception as ex:
|
||||
except Exception:
|
||||
LOG.exception('Error creating security group rules. Retrying.')
|
||||
return
|
||||
raise loopingcall.LoopingCallDone(True)
|
||||
@ -471,7 +493,7 @@ class AwsUtils:
|
||||
secgrp = self._get_ec2_resource().create_security_group(
|
||||
GroupName=name, Description=description, VpcId=vpc_id)
|
||||
if self._create_sec_grp_tags(secgrp, tags) is False:
|
||||
delete_sec_grp(secgrp.id)
|
||||
delete_sec_grp(secgrp.id) # noqa
|
||||
raise exceptions.AwsException(
|
||||
message='Timed out creating tags on security group',
|
||||
error_code='Time Out')
|
||||
@ -479,15 +501,10 @@ class AwsUtils:
|
||||
|
||||
@aws_exception
|
||||
def get_sec_group_by_id(self, secgrp_id, vpc_id=None, dry_run=False):
|
||||
filters = [{
|
||||
'Name': 'tag-value',
|
||||
'Values': [secgrp_id]
|
||||
}]
|
||||
filters = [{'Name': 'tag-value',
|
||||
'Values': [secgrp_id]}]
|
||||
if vpc_id:
|
||||
filters.append({
|
||||
'Name': 'vpc-id',
|
||||
'Values': [vpc_id]
|
||||
})
|
||||
filters.append({'Name': 'vpc-id', 'Values': [vpc_id]})
|
||||
|
||||
response = self._get_ec2_client().describe_security_groups(
|
||||
DryRun=dry_run, Filters=filters)
|
||||
@ -529,4 +546,3 @@ class AwsUtils:
|
||||
ec2_sg_id = aws_secgrp['GroupId']
|
||||
self._update_sec_group(ec2_sg_id, old_ingress, old_egress, ingress,
|
||||
egress)
|
||||
|
||||
|
@ -1,28 +1,26 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import HttpError
|
||||
from oauth2client.client import GoogleCredentials
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import reflection
|
||||
|
||||
from neutron_lib import exceptions as e
|
||||
from neutron._i18n import _
|
||||
from neutron_lib import exceptions as e
|
||||
from oslo_service import loopingcall
|
||||
from six.moves import urllib
|
||||
|
||||
@ -79,6 +77,7 @@ class GceResourceNotFound(e.NotFound):
|
||||
|
||||
def list_instances(compute, project, zone):
|
||||
"""Returns list of GCE instance resources for specified project
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -93,6 +92,7 @@ def list_instances(compute, project, zone):
|
||||
|
||||
def get_instance(compute, project, zone, instance):
|
||||
"""Get GCE instance information
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -107,6 +107,7 @@ def get_instance(compute, project, zone, instance):
|
||||
|
||||
def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
"""Wait for GCE operation to complete, raise error if operation failure
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -144,6 +145,7 @@ def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
|
||||
def get_gce_service(service_key):
|
||||
"""Returns GCE compute resource object for interacting with GCE API
|
||||
|
||||
:param service_key: string, Path of service key obtained from
|
||||
https://console.cloud.google.com/apis/credentials
|
||||
:return: :class:`Resource <Resource>` object
|
||||
@ -156,6 +158,7 @@ def get_gce_service(service_key):
|
||||
|
||||
def create_network(compute, project, name):
|
||||
"""Create network in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE Name of network
|
||||
@ -168,6 +171,7 @@ def create_network(compute, project, name):
|
||||
|
||||
def get_network(compute, project, name):
|
||||
"""Get info of network in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE Name of network
|
||||
@ -180,6 +184,7 @@ def get_network(compute, project, name):
|
||||
|
||||
def create_subnet(compute, project, region, name, ipcidr, network_link):
|
||||
"""Create subnet with particular GCE network
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -201,6 +206,7 @@ def create_subnet(compute, project, region, name, ipcidr, network_link):
|
||||
|
||||
def delete_subnet(compute, project, region, name):
|
||||
"""Delete subnet in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -214,6 +220,7 @@ def delete_subnet(compute, project, region, name):
|
||||
|
||||
def delete_network(compute, project, name):
|
||||
"""Delete network in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE network name
|
||||
@ -225,6 +232,7 @@ def delete_network(compute, project, name):
|
||||
|
||||
def create_static_ip(compute, project, region, name):
|
||||
"""Create global static IP
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -239,6 +247,7 @@ def create_static_ip(compute, project, region, name):
|
||||
|
||||
def get_static_ip(compute, project, region, name):
|
||||
"""Get static IP
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -252,6 +261,7 @@ def get_static_ip(compute, project, region, name):
|
||||
|
||||
def delete_static_ip(compute, project, region, name):
|
||||
"""Delete static IP
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -265,6 +275,7 @@ def delete_static_ip(compute, project, region, name):
|
||||
|
||||
def get_floatingip(compute, project, region, ip):
|
||||
"""Get details of static IP in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -283,6 +294,7 @@ def get_floatingip(compute, project, region, ip):
|
||||
|
||||
def allocate_floatingip(compute, project, region):
|
||||
"""Get global static IP in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -298,6 +310,7 @@ def allocate_floatingip(compute, project, region):
|
||||
|
||||
def delete_floatingip(compute, project, region, ip):
|
||||
"""Delete particular static IP
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param region: string, GCE region
|
||||
@ -311,6 +324,7 @@ def delete_floatingip(compute, project, region, ip):
|
||||
|
||||
def assign_floatingip(compute, project, zone, fixedip, floatingip):
|
||||
"""Assign static IP to interface with mentioned fixed IP
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE zone
|
||||
@ -344,6 +358,7 @@ def assign_floatingip(compute, project, zone, fixedip, floatingip):
|
||||
|
||||
def release_floatingip(compute, project, zone, floatingip):
|
||||
"""Release GCE static IP from instances using it
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE zone
|
||||
@ -376,10 +391,13 @@ def release_floatingip(compute, project, zone, floatingip):
|
||||
|
||||
def create_firewall_rule(compute, project, body):
|
||||
"""Create firewall rule in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param body: dict, Information required for creating firewall
|
||||
Refer format at https://developers.google.com/resources/api-libraries/documentation/compute/beta/python/latest/compute_beta.firewalls.html#insert
|
||||
Refer format at
|
||||
https://developers.google.com/resources/api-libraries/documentation/compute
|
||||
/beta/python/latest/compute_beta.firewalls.html#insert
|
||||
:return: Operation information
|
||||
:rtype: dict
|
||||
"""
|
||||
@ -388,11 +406,14 @@ def create_firewall_rule(compute, project, body):
|
||||
|
||||
def update_firewall_rule(compute, project, name, body):
|
||||
"""Update existing firewall rule in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE firewall name
|
||||
:param body: dict, Information required for updating firewall
|
||||
Refer format at https://developers.google.com/resources/api-libraries/documentation/compute/beta/python/latest/compute_beta.firewalls.html#update
|
||||
Refer format at
|
||||
https://developers.google.com/resources/api-libraries/documentation/
|
||||
compute/beta/python/latest/compute_beta.firewalls.html#update
|
||||
:return: Operation information
|
||||
:rtype: dict
|
||||
"""
|
||||
@ -402,6 +423,7 @@ def update_firewall_rule(compute, project, name, body):
|
||||
|
||||
def delete_firewall_rule(compute, project, name):
|
||||
"""Delete firewall rule in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE firewall name
|
||||
@ -413,6 +435,7 @@ def delete_firewall_rule(compute, project, name):
|
||||
|
||||
def get_firewall_rule(compute, project, name):
|
||||
"""Get firewall rule info in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE firewall name
|
||||
|
@ -1,19 +1,17 @@
|
||||
# Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 neutron.callbacks import events
|
||||
from neutron.callbacks import exceptions
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from oslo_log import log as logging
|
||||
@ -26,9 +24,9 @@ def subscribe(mech_driver):
|
||||
events.BEFORE_DELETE)
|
||||
registry.subscribe(mech_driver.secgroup_callback, resources.SECURITY_GROUP,
|
||||
events.BEFORE_UPDATE)
|
||||
registry.subscribe(mech_driver.secgroup_callback, resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_DELETE)
|
||||
registry.subscribe(mech_driver.secgroup_callback, resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_UPDATE)
|
||||
registry.subscribe(mech_driver.secgroup_callback, resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_CREATE)
|
||||
registry.subscribe(mech_driver.secgroup_callback,
|
||||
resources.SECURITY_GROUP_RULE, events.BEFORE_DELETE)
|
||||
registry.subscribe(mech_driver.secgroup_callback,
|
||||
resources.SECURITY_GROUP_RULE, events.BEFORE_UPDATE)
|
||||
registry.subscribe(mech_driver.secgroup_callback,
|
||||
resources.SECURITY_GROUP_RULE, events.BEFORE_CREATE)
|
||||
|
@ -1,27 +1,27 @@
|
||||
# Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 json
|
||||
import random
|
||||
from oslo_log import log
|
||||
from neutron import manager
|
||||
from neutron.callbacks import resources
|
||||
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import resources
|
||||
from neutron.common.aws_utils import AwsUtils
|
||||
from neutron import manager
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers.aws import callbacks
|
||||
from neutron_lib.exceptions import NeutronException
|
||||
from oslo_log import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -62,11 +62,14 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
|
||||
def delete_network_precommit(self, context):
|
||||
neutron_network_id = context.current['id']
|
||||
# If user is deleting an empty neutron network then nothing to be done on AWS side
|
||||
# If user is deleting an empty neutron network then nothing to be done
|
||||
# on AWS side
|
||||
if len(context.current['subnets']) > 0:
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(neutron_network_id)
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
|
||||
neutron_network_id)
|
||||
if vpc_id is not None:
|
||||
LOG.info("Deleting network %s (VPC_ID: %s)" % (neutron_network_id, vpc_id))
|
||||
LOG.info("Deleting network %s (VPC_ID: %s)" %
|
||||
(neutron_network_id, vpc_id))
|
||||
self.aws_utils.delete_vpc(vpc_id=vpc_id)
|
||||
|
||||
def delete_network_postcommit(self, context):
|
||||
@ -74,40 +77,48 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
|
||||
# SUBNET
|
||||
def create_subnet_precommit(self, context):
|
||||
LOG.info("Create subnet for network %s" % context.network.current['id'])
|
||||
LOG.info("Create subnet for network %s" %
|
||||
context.network.current['id'])
|
||||
# External Network doesn't exist on AWS, so no operations permitted
|
||||
if 'provider:physical_network' in context.network.current and \
|
||||
context.network.current['provider:physical_network'] == "external":
|
||||
if 'provider:physical_network' in context.network.current:
|
||||
if context.network.current[
|
||||
'provider:physical_network'] == "external":
|
||||
# Do not create subnets for external & provider networks. Only
|
||||
# allow tenant network subnet creation at the moment.
|
||||
LOG.info('Creating external network %s' %
|
||||
context.network.current['id'])
|
||||
LOG.info('Creating external network {0}'.format(
|
||||
context.network.current['id']))
|
||||
return
|
||||
|
||||
if context.current['ip_version'] == 6:
|
||||
raise AwsException(error_code="IPv6Error", message="Cannot create subnets with IPv6")
|
||||
raise AwsException(error_code="IPv6Error",
|
||||
message="Cannot create subnets with IPv6")
|
||||
mask = int(context.current['cidr'][-2:])
|
||||
if mask < 16 or mask > 28:
|
||||
raise AwsException(error_code="InvalidMask", message="Subnet mask has to be >16 and <28")
|
||||
raise AwsException(error_code="InvalidMask",
|
||||
message="Subnet mask has to be >16 and <28")
|
||||
try:
|
||||
# Check if this is the first subnet to be added to a network
|
||||
neutron_network = context.network.current
|
||||
associated_vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(neutron_network['id'])
|
||||
associated_vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
|
||||
neutron_network['id'])
|
||||
if associated_vpc_id is None:
|
||||
# Need to create EC2 VPC
|
||||
vpc_cidr = context.current['cidr'][:-2] + '16'
|
||||
tags = [
|
||||
{'Key': 'Name', 'Value': neutron_network['name']},
|
||||
{'Key': 'openstack_network_id', 'Value': neutron_network['id']},
|
||||
{'Key': 'openstack_tenant_id', 'Value': context.current['tenant_id']}
|
||||
{'Key': 'openstack_network_id',
|
||||
'Value': neutron_network['id']},
|
||||
{'Key': 'openstack_tenant_id',
|
||||
'Value': context.current['tenant_id']}
|
||||
]
|
||||
associated_vpc_id = self.aws_utils.create_vpc_and_tags(cidr=vpc_cidr,
|
||||
tags_list=tags)
|
||||
associated_vpc_id = self.aws_utils.create_vpc_and_tags(
|
||||
cidr=vpc_cidr, tags_list=tags)
|
||||
# Create Subnet in AWS
|
||||
tags = [
|
||||
{'Key': 'Name', 'Value': context.current['name']},
|
||||
{'Key': 'openstack_subnet_id', 'Value': context.current['id']},
|
||||
{'Key': 'openstack_tenant_id', 'Value': context.current['tenant_id']}
|
||||
{'Key': 'openstack_tenant_id',
|
||||
'Value': context.current['tenant_id']}
|
||||
]
|
||||
self.aws_utils.create_subnet_and_tags(vpc_id=associated_vpc_id,
|
||||
cidr=context.current['cidr'],
|
||||
@ -133,13 +144,16 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
pass
|
||||
|
||||
def delete_subnet_precommit(self, context):
|
||||
if 'provider:physical_network' in context.network.current and context.network.current[
|
||||
if 'provider:physical_network' in context.network.current:
|
||||
if context.network.current[
|
||||
'provider:physical_network'] == "external":
|
||||
LOG.error("Deleting provider and external networks not supported")
|
||||
LOG.error("Deleting provider and external networks not "
|
||||
"supported")
|
||||
return
|
||||
try:
|
||||
LOG.info("Deleting subnet %s" % context.current['id'])
|
||||
subnet_id = self.aws_utils.get_subnet_from_neutron_subnet_id(context.current['id'])
|
||||
subnet_id = self.aws_utils.get_subnet_from_neutron_subnet_id(
|
||||
context.current['id'])
|
||||
if subnet_id is not None:
|
||||
self.aws_utils.delete_subnet(subnet_id=subnet_id)
|
||||
except Exception as e:
|
||||
@ -156,12 +170,15 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
return
|
||||
try:
|
||||
subnets = neutron_network['subnets']
|
||||
if len(subnets) == 1 and subnets[0] == context.current['id'] or len(subnets) == 0:
|
||||
if (len(subnets) == 1 and subnets[0] == context.current['id'] or
|
||||
len(subnets) == 0):
|
||||
# Last subnet for this network was deleted, so delete VPC
|
||||
# because VPC gets created during first subnet creation under
|
||||
# an OpenStack network
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(neutron_network['id'])
|
||||
LOG.info("Deleting VPC %s since this was the last subnet in the vpc" % vpc_id)
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
|
||||
neutron_network['id'])
|
||||
LOG.info("Deleting VPC %s since this was the last subnet in "
|
||||
"the vpc" % vpc_id)
|
||||
self.aws_utils.delete_vpc(vpc_id=vpc_id)
|
||||
except Exception as e:
|
||||
LOG.error("Error in delete subnet postcommit: %s" % e)
|
||||
@ -195,12 +212,9 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
fixed_ip_dict['subnet_id'])
|
||||
secgroup_ids = context.current['security_groups']
|
||||
self.create_security_groups_if_needed(context, secgroup_ids)
|
||||
|
||||
segment_id = random.choice(context.network.network_segments)[api.ID]
|
||||
context.set_binding(segment_id,
|
||||
"vip_type_a",
|
||||
json.dumps(fixed_ip_dict),
|
||||
status='ACTIVE')
|
||||
context.set_binding(segment_id, "vip_type_a",
|
||||
json.dumps(fixed_ip_dict), status='ACTIVE')
|
||||
return True
|
||||
|
||||
def create_security_groups_if_needed(self, context, secgrp_ids):
|
||||
@ -279,5 +293,3 @@ class AwsMechanismDriver(api.MechanismDriver):
|
||||
elif event == events.BEFORE_UPDATE:
|
||||
rule_id = kwargs['security_group_rule_id']
|
||||
self.update_security_group_rules(context, rule_id)
|
||||
|
||||
|
||||
|
@ -1,32 +1,31 @@
|
||||
# Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 random
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
import ipaddr
|
||||
from neutron._i18n import _
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from neutron.common import gceconf
|
||||
from neutron.common import gceutils
|
||||
from neutron.extensions import securitygroup as sg
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.extensions import securitygroup as sg
|
||||
from neutron_lib import exceptions as e
|
||||
from oslo_log import log
|
||||
|
||||
import ipaddr
|
||||
try:
|
||||
from neutron_lib.plugins import directory
|
||||
except ImportError:
|
||||
@ -110,7 +109,9 @@ class GceMechanismDriver(api.MechanismDriver):
|
||||
pass
|
||||
|
||||
def create_subnet_postcommit(self, context):
|
||||
compute, project, region = self.gce_svc, self.gce_project, self.gce_region
|
||||
compute = self.gce_svc
|
||||
project = self.gce_project
|
||||
region = self.gce_region
|
||||
network_name = self._gce_subnet_network_name(context)
|
||||
name = self._gce_subnet_name(context)
|
||||
cidr = context.current['cidr']
|
||||
@ -132,7 +133,9 @@ class GceMechanismDriver(api.MechanismDriver):
|
||||
pass
|
||||
|
||||
def delete_subnet_postcommit(self, context):
|
||||
compute, project, region = self.gce_svc, self.gce_project, self.gce_region
|
||||
compute = self.gce_svc
|
||||
project = self.gce_project
|
||||
region = self.gce_region
|
||||
cidr = context.current['cidr']
|
||||
if self.is_private_network(cidr):
|
||||
name = self._gce_subnet_name(context)
|
||||
|
@ -1,18 +1,19 @@
|
||||
# Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2016 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 neutron.common.aws_utils import AwsUtils
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.common import exceptions
|
||||
from neutron.db import common_db_mixin
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_db
|
||||
@ -20,23 +21,19 @@ from neutron.db import l3_dvrscheduler_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import l3_hamode_db
|
||||
from neutron.db import l3_hascheduler_db
|
||||
from neutron.db import securitygroups_db
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.quota import resource_registry
|
||||
from neutron.services import service_base
|
||||
from oslo_log import log as logging
|
||||
from neutron.common import exceptions
|
||||
from neutron.db import securitygroups_db
|
||||
|
||||
from neutron.common.aws_utils import AwsUtils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
common_db_mixin.CommonDbMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
l3_dvrscheduler_db.L3_DVRsch_db_mixin,
|
||||
|
||||
class AwsRouterPlugin(
|
||||
service_base.ServicePluginBase, common_db_mixin.CommonDbMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin,
|
||||
l3_hascheduler_db.L3_HA_scheduler_db_mixin):
|
||||
"""Implementation of the Neutron L3 Router Service Plugin.
|
||||
|
||||
@ -47,12 +44,13 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin.
|
||||
"""
|
||||
supported_extension_aliases = ["dvr", "router", "ext-gw-mode",
|
||||
"extraroute", "l3_agent_scheduler",
|
||||
"l3-ha", "security-group"]
|
||||
supported_extension_aliases = [
|
||||
"dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler",
|
||||
"l3-ha", "security-group"
|
||||
]
|
||||
|
||||
@resource_registry.tracked_resources(router=l3_db.Router,
|
||||
floatingip=l3_db.FloatingIP,
|
||||
@resource_registry.tracked_resources(
|
||||
router=l3_db.Router, floatingip=l3_db.FloatingIP,
|
||||
security_group=securitygroups_db.SecurityGroup)
|
||||
def __init__(self):
|
||||
self.aws_utils = AwsUtils()
|
||||
@ -68,7 +66,7 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
" between (L2) Neutron networks and access to external"
|
||||
" networks via a NAT gateway.")
|
||||
|
||||
########## FLOATING IP FEATURES ###############
|
||||
# FLOATING IP FEATURES
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
try:
|
||||
@ -76,12 +74,15 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
public_ip_allocated = response['PublicIp']
|
||||
LOG.info("Created elastic IP %s" % public_ip_allocated)
|
||||
if 'floatingip' in floatingip:
|
||||
floatingip['floatingip']['floating_ip_address'] = public_ip_allocated
|
||||
floatingip['floatingip'][
|
||||
'floating_ip_address'] = public_ip_allocated
|
||||
|
||||
if 'port_id' in floatingip['floatingip'] and floatingip['floatingip']['port_id'] is not None:
|
||||
if ('port_id' in floatingip['floatingip'] and
|
||||
floatingip['floatingip']['port_id'] is not None):
|
||||
# Associate to a Port
|
||||
port_id = floatingip['floatingip']['port_id']
|
||||
self._associate_floatingip_to_port(context, public_ip_allocated, port_id)
|
||||
self._associate_floatingip_to_port(
|
||||
context, public_ip_allocated, port_id)
|
||||
except Exception as e:
|
||||
LOG.error("Error in Creation/Allocating EIP")
|
||||
if public_ip_allocated:
|
||||
@ -94,57 +95,73 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
context, floatingip,
|
||||
initial_status=n_const.FLOATINGIP_STATUS_DOWN)
|
||||
except Exception as e:
|
||||
LOG.error("Error when adding floating ip in openstack. Deleting Elastic IP: %s" % public_ip_allocated)
|
||||
LOG.error("Error when adding floating ip in openstack. "
|
||||
"Deleting Elastic IP: %s" % public_ip_allocated)
|
||||
self.aws_utils.delete_elastic_ip(public_ip_allocated)
|
||||
raise e
|
||||
return res
|
||||
|
||||
def _associate_floatingip_to_port(self, context, floating_ip_address, port_id):
|
||||
def _associate_floatingip_to_port(self, context, floating_ip_address,
|
||||
port_id):
|
||||
port = self._core_plugin.get_port(context, port_id)
|
||||
ec2_id = None
|
||||
fixed_ip_address = None
|
||||
# TODO: Assuming that there is only one fixed IP
|
||||
# TODO(fixed_ip): Assuming that there is only one fixed IP
|
||||
if len(port['fixed_ips']) > 0:
|
||||
fixed_ip = port['fixed_ips'][0]
|
||||
if 'ip_address' in fixed_ip:
|
||||
fixed_ip_address = fixed_ip['ip_address']
|
||||
search_opts = {'ip': fixed_ip_address, 'tenant_id': context.tenant_id}
|
||||
server_list = self.aws_utils.get_nova_client().servers.list(search_opts=search_opts)
|
||||
search_opts = {
|
||||
'ip': fixed_ip_address,
|
||||
'tenant_id': context.tenant_id
|
||||
}
|
||||
server_list = self.aws_utils.get_nova_client().servers.list(
|
||||
search_opts=search_opts)
|
||||
if len(server_list) > 0:
|
||||
server = server_list[0]
|
||||
if 'ec2_id' in server.metadata:
|
||||
ec2_id = server.metadata['ec2_id']
|
||||
if floating_ip_address is not None and ec2_id is not None:
|
||||
self.aws_utils.associate_elastic_ip_to_ec2_instance(floating_ip_address, ec2_id)
|
||||
LOG.info("EC2 ID found for IP %s : %s" % (fixed_ip_address, ec2_id))
|
||||
self.aws_utils.associate_elastic_ip_to_ec2_instance(
|
||||
floating_ip_address, ec2_id)
|
||||
LOG.info("EC2 ID found for IP %s : %s" % (fixed_ip_address,
|
||||
ec2_id))
|
||||
else:
|
||||
LOG.warning("EC2 ID not found to associate the floating IP")
|
||||
raise exceptions.AwsException(error_code="No Server Found",
|
||||
raise exceptions.AwsException(
|
||||
error_code="No Server Found",
|
||||
message="No server found with the Required IP")
|
||||
|
||||
def update_floatingip(self, context, id, floatingip):
|
||||
floating_ip_dict = super(AwsRouterPlugin, self).get_floatingip(context, id)
|
||||
if 'floatingip' in floatingip and 'port_id' in floatingip['floatingip']:
|
||||
floating_ip_dict = super(AwsRouterPlugin, self).get_floatingip(
|
||||
context, id)
|
||||
if ('floatingip' in floatingip and
|
||||
'port_id' in floatingip['floatingip']):
|
||||
port_id = floatingip['floatingip']['port_id']
|
||||
if port_id is not None:
|
||||
# Associate Floating IP
|
||||
LOG.info("Associating elastic IP %s with port %s" %
|
||||
(floating_ip_dict['floating_ip_address'], port_id))
|
||||
self._associate_floatingip_to_port(context,
|
||||
floating_ip_dict['floating_ip_address'], port_id)
|
||||
self._associate_floatingip_to_port(
|
||||
context, floating_ip_dict['floating_ip_address'], port_id)
|
||||
else:
|
||||
try:
|
||||
# Port Disassociate
|
||||
self.aws_utils.disassociate_elastic_ip_from_ec2_instance(floating_ip_dict['floating_ip_address'])
|
||||
self.aws_utils.disassociate_elastic_ip_from_ec2_instance(
|
||||
floating_ip_dict['floating_ip_address'])
|
||||
except exceptions.AwsException as e:
|
||||
if 'Association ID not found' in e.msg:
|
||||
# Since its already disassociated on EC2, we continue and remove the association here.
|
||||
LOG.warn("Association for Elastic IP not found. Probable out of band change on EC2.")
|
||||
# Since its already disassociated on EC2, we continue
|
||||
# and remove the association here.
|
||||
LOG.warn("Association for Elastic IP not found. "
|
||||
"Probable out of band change on EC2.")
|
||||
elif 'InvalidAddress.NotFound' in e.msg:
|
||||
LOG.warn("Elastic IP cannot be found in EC2. Probably removed out of band on EC2.")
|
||||
LOG.warn("Elastic IP cannot be found in EC2. Probably "
|
||||
"removed out of band on EC2.")
|
||||
else:
|
||||
raise e
|
||||
return super(AwsRouterPlugin, self).update_floatingip(context, id, floatingip)
|
||||
return super(AwsRouterPlugin, self).update_floatingip(
|
||||
context, id, floatingip)
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
floating_ip = super(AwsRouterPlugin, self).get_floatingip(context, id)
|
||||
@ -159,17 +176,21 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
raise e
|
||||
return super(AwsRouterPlugin, self).delete_floatingip(context, id)
|
||||
|
||||
##### ROUTERS #####
|
||||
# ROUTERS
|
||||
|
||||
def create_router(self, context, router):
|
||||
try:
|
||||
router_name = router['router']['name']
|
||||
internet_gw_res = self.aws_utils.create_internet_gateway_resource()
|
||||
ret_obj = super(AwsRouterPlugin, self).create_router(context, router)
|
||||
internet_gw_res.create_tags(Tags=[
|
||||
{'Key': 'Name', 'Value': router_name},
|
||||
{'Key': 'openstack_router_id', 'Value': ret_obj['id']}
|
||||
])
|
||||
ret_obj = super(AwsRouterPlugin, self).create_router(
|
||||
context, router)
|
||||
internet_gw_res.create_tags(Tags=[{
|
||||
'Key': 'Name',
|
||||
'Value': router_name
|
||||
}, {
|
||||
'Key': 'openstack_router_id',
|
||||
'Value': ret_obj['id']
|
||||
}])
|
||||
LOG.info("Created AWS router %s with openstack id %s" %
|
||||
(router_name, ret_obj['id']))
|
||||
return ret_obj
|
||||
@ -188,22 +209,27 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
return super(AwsRouterPlugin, self).delete_router(context, id)
|
||||
|
||||
def update_router(self, context, id, router):
|
||||
## get internet gateway resource by openstack router id and update the tags
|
||||
# get internet gateway resource by openstack router id and update the
|
||||
# tags
|
||||
try:
|
||||
if 'router' in router and 'name' in router['router']:
|
||||
router_name = router['router']['name']
|
||||
tags_list = [
|
||||
{'Key': 'Name', 'Value': router_name},
|
||||
{'Key': 'openstack_router_id', 'Value': id}
|
||||
]
|
||||
tags_list = [{
|
||||
'Key': 'Name',
|
||||
'Value': router_name
|
||||
}, {
|
||||
'Key': 'openstack_router_id',
|
||||
'Value': id
|
||||
}]
|
||||
LOG.info("Updated router %s" % id)
|
||||
self.aws_utils.create_tags_internet_gw_from_router_id(id, tags_list)
|
||||
self.aws_utils.create_tags_internet_gw_from_router_id(
|
||||
id, tags_list)
|
||||
except Exception as e:
|
||||
LOG.error("Error in Updating Router: %s " % e)
|
||||
raise e
|
||||
return super(AwsRouterPlugin, self).update_router(context, id, router)
|
||||
|
||||
###### ROUTER INTERFACE ######
|
||||
# ROUTER INTERFACE
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
subnet_id = interface_info['subnet_id']
|
||||
@ -214,32 +240,44 @@ class AwsRouterPlugin(service_base.ServicePluginBase,
|
||||
# Get Internet Gateway ID
|
||||
ig_id = self.aws_utils.get_internet_gw_from_router_id(router_id)
|
||||
# Get VPC ID
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(neutron_network_id)
|
||||
vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
|
||||
neutron_network_id)
|
||||
self.aws_utils.attach_internet_gateway(ig_id, vpc_id)
|
||||
# Search for a Route table tagged with Router-id
|
||||
route_tables = self.aws_utils.get_route_table_by_router_id(router_id)
|
||||
route_tables = self.aws_utils.get_route_table_by_router_id(
|
||||
router_id)
|
||||
if len(route_tables) == 0:
|
||||
# If not tagged, Fetch all the Route Tables Select one and tag it
|
||||
route_tables = self.aws_utils.describe_route_tables_by_vpc_id(vpc_id)
|
||||
# If not tagged, Fetch all the Route Tables Select one and tag
|
||||
# it
|
||||
route_tables = self.aws_utils.describe_route_tables_by_vpc_id(
|
||||
vpc_id)
|
||||
if len(route_tables) > 0:
|
||||
route_table = route_tables[0]
|
||||
route_table_res = self.aws_utils._get_ec2_resource().RouteTable(route_table['RouteTableId'])
|
||||
route_table_res.create_tags(Tags=[
|
||||
{'Key': 'openstack_router_id', 'Value': router_id}
|
||||
])
|
||||
route_table_res = self.aws_utils._get_ec2_resource(
|
||||
).RouteTable(route_table['RouteTableId'])
|
||||
route_table_res.create_tags(Tags=[{
|
||||
'Key':
|
||||
'openstack_router_id',
|
||||
'Value':
|
||||
router_id
|
||||
}])
|
||||
if len(route_tables) > 0:
|
||||
route_table = route_tables[0]
|
||||
self.aws_utils.create_default_route_to_ig(route_table['RouteTableId'], ig_id, ignore_errors=True)
|
||||
self.aws_utils.create_default_route_to_ig(
|
||||
route_table['RouteTableId'], ig_id, ignore_errors=True)
|
||||
except Exception as e:
|
||||
LOG.error("Error in Creating Interface: %s " % e)
|
||||
raise e
|
||||
return super(AwsRouterPlugin, self).add_router_interface(context, router_id, interface_info)
|
||||
return super(AwsRouterPlugin, self).add_router_interface(
|
||||
context, router_id, interface_info)
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
LOG.info("Deleting port %s from router %s" % (interface_info['port_id'], router_id))
|
||||
LOG.info("Deleting port %s from router %s" %
|
||||
(interface_info['port_id'], router_id))
|
||||
self.aws_utils.detach_internet_gateway_by_router_id(router_id)
|
||||
route_tables = self.aws_utils.get_route_table_by_router_id(router_id)
|
||||
if route_tables:
|
||||
route_table_id = route_tables[0]['RouteTableId']
|
||||
self.aws_utils.delete_default_route_to_ig(route_table_id)
|
||||
return super(AwsRouterPlugin, self).remove_router_interface(context, router_id, interface_info)
|
||||
return super(AwsRouterPlugin, self).remove_router_interface(
|
||||
context, router_id, interface_info)
|
||||
|
@ -1,21 +1,19 @@
|
||||
# Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 neutron_lib
|
||||
from distutils.version import LooseVersion
|
||||
from oslo_log import log as logging
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from neutron.common import exceptions
|
||||
from neutron.common import gceconf
|
||||
from neutron.common import gceutils
|
||||
@ -30,6 +28,7 @@ from neutron.plugins.common import constants
|
||||
from neutron.quota import resource_registry
|
||||
from neutron.services import service_base
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -37,17 +36,19 @@ if LooseVersion(neutron_lib.__version__) < LooseVersion("1.0.0"):
|
||||
router = l3_db.Router
|
||||
floating_ip = l3_db.FloatingIP
|
||||
plugin_type = constants.L3_ROUTER_NAT
|
||||
service_plugin_class = service_base.ServicePluginBase
|
||||
else:
|
||||
from neutron.db.models import l3
|
||||
from neutron_lib.plugins import constants as plugin_constants
|
||||
from neutron_lib.services import base as service_base
|
||||
from neutron_lib.services import base
|
||||
router = l3.Router
|
||||
floating_ip = l3.FloatingIP
|
||||
plugin_type = plugin_constants.L3
|
||||
service_plugin_class = base.ServicePluginBase
|
||||
|
||||
|
||||
class GceRouterPlugin(
|
||||
service_base.ServicePluginBase, common_db_mixin.CommonDbMixin,
|
||||
service_plugin_class, common_db_mixin.CommonDbMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin,
|
||||
l3_hascheduler_db.L3_HA_scheduler_db_mixin):
|
||||
@ -93,7 +94,9 @@ class GceRouterPlugin(
|
||||
LOG.info('Released GCE static IP %s' % floatingip)
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
compute, project, region = self.gce_svc, self.gce_project, self.gce_region
|
||||
compute = self.gce_svc
|
||||
project = self.gce_project
|
||||
region = self.gce_region
|
||||
public_ip_allocated = None
|
||||
|
||||
try:
|
||||
@ -154,7 +157,9 @@ class GceRouterPlugin(
|
||||
context, id)
|
||||
public_ip_allocated = orig_floatingip['floating_ip_address']
|
||||
port_id = floatingip_dict['port_id']
|
||||
compute, project, region = self.gce_svc, self.gce_project, self.gce_region
|
||||
compute = self.gce_svc
|
||||
project = self.gce_project
|
||||
region = self.gce_region
|
||||
gceutils.release_floatingip(compute, project, region,
|
||||
public_ip_allocated)
|
||||
if port_id:
|
||||
@ -166,7 +171,9 @@ class GceRouterPlugin(
|
||||
def delete_floatingip(self, context, id):
|
||||
floating_ip = super(GceRouterPlugin, self).get_floatingip(context, id)
|
||||
public_ip_allocated = floating_ip['floating_ip_address']
|
||||
compute, project, region = self.gce_svc, self.gce_project, self.gce_region
|
||||
compute = self.gce_svc
|
||||
project = self.gce_project
|
||||
region = self.gce_region
|
||||
self._cleanup_floatingip(compute, project, region, public_ip_allocated)
|
||||
return super(GceRouterPlugin, self).delete_floatingip(context, id)
|
||||
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
@ -1,32 +1,32 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import mock
|
||||
import os
|
||||
|
||||
from neutron.tests import base
|
||||
from neutron.plugins.ml2.drivers.gce.mech_gce import GceMechanismDriver
|
||||
from neutron.plugins.ml2.drivers.gce.mech_gce import SecurityGroupInvalidDirection
|
||||
from neutron.extensions import securitygroup as sg
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.ml2.drivers.gce.mech_gce import GceMechanismDriver
|
||||
from neutron.plugins.ml2.drivers.gce.mech_gce import SecurityGroupInvalidDirection # noqa
|
||||
from neutron.tests import base
|
||||
from neutron.tests.common.gce import gce_mock
|
||||
from neutron.tests.common.gce.gce_mock import FakeNeutronManager
|
||||
from neutron.tests.unit.extensions import test_securitygroup as test_sg
|
||||
from neutron.extensions import securitygroup as sg
|
||||
from neutron_lib import constants as const
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath("gce_mock.py")) + '/data'
|
||||
NETWORK_LINK = "projects/omni-163105/global/networks/net-03c4f178-670e-4805-a511-9470ca4a0b06"
|
||||
NETWORKS_LINK = "projects/omni-163105/global/networks"
|
||||
NETWORK_LINK = NETWORKS_LINK + "/net-03c4f178-670e-4805-a511-9470ca4a0b06"
|
||||
|
||||
if hasattr(NeutronManager, "get_plugin"):
|
||||
neutron_get_plugin = 'neutron.manager.NeutronManager.get_plugin'
|
||||
|
@ -1,39 +1,35 @@
|
||||
# Copyright 2016 Platform9 Systems 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.
|
||||
"""
|
||||
Copyright 2016 Platform9 Systems 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 moto import mock_ec2
|
||||
from moto import mock_cloudwatch
|
||||
from moto.ec2 import ec2_backends
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.compute import power_state
|
||||
from nova.compute import vm_states
|
||||
from nova.compute import task_states
|
||||
from nova.image import glance
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit import matchers
|
||||
from nova.virt.ec2 import EC2Driver
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import base64
|
||||
import boto
|
||||
import contextlib
|
||||
import mock
|
||||
|
||||
from moto import mock_cloudwatch
|
||||
from moto import mock_ec2
|
||||
from nova.compute import task_states
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit import matchers
|
||||
from nova.virt.ec2 import EC2Driver
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
if hasattr(glance, "GlanceImageService"):
|
||||
from nova.image.glance import GlanceImageService
|
||||
else:
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import six
|
||||
|
@ -1,28 +1,27 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import mock
|
||||
import os
|
||||
|
||||
from nova import test
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.virt import fake
|
||||
from nova.virt.gce.driver import GCEDriver
|
||||
from nova.tests.unit.virt.gce import gce_mock
|
||||
from nova.tests.unit.virt.gce.gce_mock import FakeImageService
|
||||
from nova.virt import fake
|
||||
from nova.virt.gce.driver import GCEDriver
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + '/data'
|
||||
|
||||
|
@ -1,17 +1,16 @@
|
||||
# Copyright (c) 2014 Thoughtworks.
|
||||
# Copyright (c) 2016 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2014 Thoughtworks.
|
||||
Copyright (c) 2016 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from nova.virt.ec2 import ec2driver
|
||||
|
||||
|
@ -1,35 +1,36 @@
|
||||
# Copyright (c) 2014 ThoughtWorks
|
||||
# Copyright (c) 2016 Platform9 Systems 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.
|
||||
"""
|
||||
Copyright (c) 2014 ThoughtWorks
|
||||
Copyright (c) 2016 Platform9 Systems 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 oslo.config import cfg
|
||||
from nova import db
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.scheduler import filters
|
||||
from oslo.config import cfg
|
||||
|
||||
opts = [
|
||||
cfg.BoolOpt('cloud_burst',
|
||||
help='Switch to enable could bursting'),
|
||||
cfg.StrOpt('cloud_burst_availability_zone',
|
||||
help='The availability zone of only compute hosts with the public cloud driver'),
|
||||
cfg.BoolOpt('cloud_burst', help='Switch to enable could bursting'),
|
||||
cfg.StrOpt(
|
||||
'cloud_burst_availability_zone',
|
||||
help='Availability zone of compute hosts with the public cloud driver'
|
||||
),
|
||||
]
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CloudBurstFilter(filters.BaseHostFilter):
|
||||
"""Filter for cloud burst availability zone"""
|
||||
|
||||
@ -37,11 +38,11 @@ class CloudBurstFilter(filters.BaseHostFilter):
|
||||
|
||||
def host_passes(self, host_state, filter_properties):
|
||||
context = filter_properties['context'].elevated()
|
||||
metadata = db.aggregate_metadata_get_by_host(context, host_state.host, key='availability_zone')
|
||||
metadata = db.aggregate_metadata_get_by_host(context, host_state.host,
|
||||
key='availability_zone')
|
||||
|
||||
if CONF.cloud_burst:
|
||||
return CONF.cloud_burst_availability_zone in metadata['availability_zone']
|
||||
else:
|
||||
return CONF.cloud_burst_availability_zone not in metadata['availability_zone']
|
||||
|
||||
return True
|
||||
return CONF.cloud_burst_availability_zone in metadata[
|
||||
'availability_zone']
|
||||
return CONF.cloud_burst_availability_zone not in metadata[
|
||||
'availability_zone']
|
||||
|
@ -1,52 +1,49 @@
|
||||
# Copyright (c) 2014 Thoughtworks.
|
||||
# Copyright (c) 2016 Platform9 Systems 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Connection to the Amazon Web Services - EC2 service"""
|
||||
"""
|
||||
Copyright (c) 2014 Thoughtworks.
|
||||
Copyright (c) 2016 Platform9 Systems 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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import boto.ec2.cloudwatch
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from threading import Lock
|
||||
from boto import ec2, vpc
|
||||
|
||||
from boto import ec2
|
||||
from boto import exception as boto_exc
|
||||
from boto.exception import EC2ResponseError
|
||||
from boto.regioninfo import RegionInfo
|
||||
from oslo_config import cfg
|
||||
from nova.i18n import *
|
||||
from nova import block_device
|
||||
from nova.compute import power_state
|
||||
from nova.compute import task_states
|
||||
from nova.console import type as ctype
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.image import glance
|
||||
from nova.virt import driver
|
||||
from nova.virt.ec2.exception_handler import Ec2ExceptionHandler
|
||||
from nova.virt import hardware
|
||||
from nova.virt import virtapi
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
from nova.virt import driver
|
||||
from nova.virt import virtapi
|
||||
from nova.virt import hardware
|
||||
from nova.virt.ec2.exception_handler import Ec2ExceptionHandler
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
aws_group = cfg.OptGroup(name='AWS', title='Options to connect to an AWS cloud')
|
||||
aws_group = cfg.OptGroup(name='AWS',
|
||||
title='Options to connect to an AWS cloud')
|
||||
|
||||
aws_opts = [
|
||||
cfg.StrOpt('secret_key', help='Secret key of AWS account', secret=True),
|
||||
@ -126,7 +123,7 @@ EC2_FLAVOR_MAP = {
|
||||
't2.small': {'memory_mb': 2048.0, 'vcpus': 1},
|
||||
'x1.32xlarge': {'memory_mb': 1998848.0, 'vcpus': 128}
|
||||
}
|
||||
|
||||
_EC2_NODES = None
|
||||
|
||||
DIAGNOSTIC_KEYS_TO_FILTER = ['group', 'block_device_mapping']
|
||||
|
||||
@ -175,7 +172,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
'cpu_info': {},
|
||||
'disk_available_least': CONF.AWS.max_disk_gb,
|
||||
}
|
||||
|
||||
global _EC2_NODES
|
||||
self._mounts = {}
|
||||
self._interfaces = {}
|
||||
self._uuid_to_ec2_instance = {}
|
||||
@ -184,7 +181,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
aws_endpoint = "ec2." + aws_region + ".amazonaws.com"
|
||||
|
||||
region = RegionInfo(name=aws_region, endpoint=aws_endpoint)
|
||||
self.ec2_conn = ec2.EC2Connection(aws_access_key_id=CONF.AWS.access_key,
|
||||
self.ec2_conn = ec2.EC2Connection(
|
||||
aws_access_key_id=CONF.AWS.access_key,
|
||||
aws_secret_access_key=CONF.AWS.secret_key,
|
||||
region=region)
|
||||
|
||||
@ -193,19 +191,17 @@ class EC2Driver(driver.ComputeDriver):
|
||||
aws_secret_access_key=CONF.AWS.secret_key)
|
||||
|
||||
LOG.info("EC2 driver init with %s region" % aws_region)
|
||||
if not '_EC2_NODES' in globals():
|
||||
if _EC2_NODES is None:
|
||||
set_nodes([CONF.host])
|
||||
|
||||
def init_host(self, host):
|
||||
"""
|
||||
Initialize anything that is necessary for the driver to function,
|
||||
"""Initialize anything that is necessary for the driver to function,
|
||||
including catching up with currently running VM's on the given host.
|
||||
"""
|
||||
return
|
||||
|
||||
def list_instances(self):
|
||||
"""
|
||||
Return the names of all the instances known to the virtualization
|
||||
"""Return the names of all the instances known to the virtualization
|
||||
layer, as a list.
|
||||
"""
|
||||
all_instances = self.ec2_conn.get_only_instances()
|
||||
@ -217,8 +213,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
continue
|
||||
if len(instance.tags) > 0:
|
||||
if 'openstack_id' in instance.tags:
|
||||
self._uuid_to_ec2_instance[instance.tags['openstack_id']] = \
|
||||
instance
|
||||
self._uuid_to_ec2_instance[
|
||||
instance.tags['openstack_id']] = instance
|
||||
else:
|
||||
generate_uuid = True
|
||||
else:
|
||||
@ -238,13 +234,14 @@ class EC2Driver(driver.ComputeDriver):
|
||||
pass
|
||||
|
||||
def _add_ssh_keys(self, key_name, key_data):
|
||||
"""
|
||||
Adds SSH Keys into AWS EC2 account
|
||||
"""Adds SSH Keys into AWS EC2 account
|
||||
|
||||
:param key_name:
|
||||
:param key_data:
|
||||
:return:
|
||||
"""
|
||||
# TODO: Need to handle the cases if a key with the same keyname exists and different key content
|
||||
# TODO(add_ssh_keys): Need to handle the cases if a key with the same
|
||||
# keyname exists and different key content
|
||||
exist_key_pair = self.ec2_conn.get_key_pair(key_name)
|
||||
if not exist_key_pair:
|
||||
LOG.info("Adding SSH key to AWS")
|
||||
@ -253,8 +250,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
LOG.info("SSH key already exists in AWS")
|
||||
|
||||
def _get_image_ami_id_from_meta(self, context, image_lacking_meta):
|
||||
"""
|
||||
Pulls the Image AMI ID from the location attribute of Image Meta
|
||||
"""Pulls the Image AMI ID from the location attribute of Image Meta
|
||||
|
||||
:param image_meta:
|
||||
:return: ami_id
|
||||
"""
|
||||
@ -266,14 +263,17 @@ class EC2Driver(driver.ComputeDriver):
|
||||
return image_meta['aws_image_id']
|
||||
except Exception as e:
|
||||
LOG.error("Error in parsing Image Id: %s" % e)
|
||||
raise exception.BuildAbortException("Invalid or Non-Existent Image ID Error")
|
||||
raise exception.BuildAbortException("Invalid or Non-Existent Image"
|
||||
" ID Error")
|
||||
|
||||
def _process_network_info(self, network_info):
|
||||
"""
|
||||
Will process network_info object by picking up only one Network out of many
|
||||
"""Will process network_info object by picking up only one Network out
|
||||
of many
|
||||
|
||||
:param network_info:
|
||||
:return:
|
||||
"""
|
||||
|
||||
LOG.info("Networks to be processed : %s" % network_info)
|
||||
subnet_id = None
|
||||
fixed_ip = None
|
||||
@ -337,16 +337,18 @@ class EC2Driver(driver.ComputeDriver):
|
||||
subnet_id, fixed_ip, port_id, network_id = self._process_network_info(
|
||||
network_info)
|
||||
if subnet_id is None or fixed_ip is None:
|
||||
raise exception.BuildAbortException("Network configuration failure")
|
||||
|
||||
security_groups = self._get_instance_sec_grps(context, port_id, network_id)
|
||||
raise exception.BuildAbortException("Network configuration "
|
||||
"failure")
|
||||
|
||||
security_groups = self._get_instance_sec_grps(context, port_id,
|
||||
network_id)
|
||||
# Flavor
|
||||
flavor_dict = instance['flavor']
|
||||
flavor_type = flavor_dict['name']
|
||||
|
||||
# SSH Keys
|
||||
if instance['key_name'] is not None and instance['key_data'] is not None:
|
||||
if (instance['key_name'] is not None and
|
||||
instance['key_data'] is not None):
|
||||
self._add_ssh_keys(instance['key_name'], instance['key_data'])
|
||||
|
||||
# Creating the EC2 instance
|
||||
@ -356,7 +358,6 @@ class EC2Driver(driver.ComputeDriver):
|
||||
if 'user_data' in instance and instance['user_data'] is not None:
|
||||
user_data = instance['user_data']
|
||||
user_data = base64.b64decode(user_data)
|
||||
|
||||
try:
|
||||
reservation = self.ec2_conn.run_instances(
|
||||
instance_type=flavor_type, key_name=instance['key_name'],
|
||||
@ -366,7 +367,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
ec2_instance = reservation.instances
|
||||
ec2_instance_obj = ec2_instance[0]
|
||||
ec2_id = ec2_instance[0].id
|
||||
self._wait_for_state(instance, ec2_id, "running", power_state.RUNNING)
|
||||
self._wait_for_state(instance, ec2_id, "running",
|
||||
power_state.RUNNING)
|
||||
instance['metadata'].update({'ec2_id': ec2_id})
|
||||
ec2_instance_obj.add_tag("Name", instance['display_name'])
|
||||
ec2_instance_obj.add_tag("openstack_id", instance['uuid'])
|
||||
@ -377,9 +379,11 @@ class EC2Driver(driver.ComputeDriver):
|
||||
if len(instances) > 0:
|
||||
public_ip = instances[0].ip_address
|
||||
if public_ip is not None:
|
||||
instance['metadata'].update({'public_ip_address': public_ip})
|
||||
instance['metadata'].update({
|
||||
'public_ip_address': public_ip})
|
||||
except EC2ResponseError as ec2_exception:
|
||||
actual_exception = Ec2ExceptionHandler.get_processed_exception(ec2_exception)
|
||||
actual_exception = Ec2ExceptionHandler.get_processed_exception(
|
||||
ec2_exception)
|
||||
LOG.info("Error in starting instance %s" % (actual_exception))
|
||||
raise exception.BuildAbortException(actual_exception.message)
|
||||
|
||||
@ -390,19 +394,20 @@ class EC2Driver(driver.ComputeDriver):
|
||||
return self._uuid_to_ec2_instance[instance.uuid].id
|
||||
# if none of the conditions are met we cannot map OpenStack UUID to
|
||||
# AWS ID.
|
||||
raise exception.InstanceNotFound('Instance %s not found' % instance.uuid)
|
||||
|
||||
raise exception.InstanceNotFound('Instance {0} not found'.format(
|
||||
instance.uuid))
|
||||
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
"""Snapshot an image of the specified instance
|
||||
on EC2 and create an Image which gets stored in AMI (internally in EBS Snapshot)
|
||||
"""Snapshot an image of the specified instance on EC2 and create an
|
||||
Image which gets stored in AMI (internally in EBS Snapshot)
|
||||
|
||||
:param context: security context
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param image_id: Reference to a pre-created image that will hold the snapshot.
|
||||
:param image_id: Reference to a pre-created image that will hold the
|
||||
snapshot.
|
||||
"""
|
||||
if instance.metadata.get('ec2_id', None) is None:
|
||||
raise exception.InstanceNotFound(instance_id=instance['uuid'])
|
||||
|
||||
# Adding the below line only alters the state of the instance and not
|
||||
# its image in OpenStack.
|
||||
update_task_state(
|
||||
@ -418,7 +423,6 @@ class EC2Driver(driver.ComputeDriver):
|
||||
name=str(image_id), description="Image created by OpenStack",
|
||||
no_reboot=False, dry_run=False)
|
||||
LOG.info("Image created: %s." % ec2_image_id)
|
||||
|
||||
# The instance will be in pending state when it comes up, waiting
|
||||
# for it to be in available
|
||||
self._wait_for_image_state(ec2_image_id, "available")
|
||||
@ -433,8 +437,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
'image_state': 'available',
|
||||
'owner_id': instance['project_id'],
|
||||
'ramdisk_id': instance['ramdisk_id'],
|
||||
'ec2_image_id': ec2_image_id }
|
||||
}
|
||||
'ec2_image_id': ec2_image_id}}
|
||||
# TODO(jhurt): This currently fails, leaving the status of an instance
|
||||
# as 'snapshotting'
|
||||
image_api.update(context, image_id, metadata)
|
||||
@ -464,12 +467,14 @@ class EC2Driver(driver.ComputeDriver):
|
||||
self._hard_reboot(
|
||||
context, instance, network_info, block_device_info)
|
||||
|
||||
def _soft_reboot(self, context, instance, network_info, block_device_info=None):
|
||||
def _soft_reboot(self, context, instance, network_info,
|
||||
block_device_info=None):
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
self.ec2_conn.reboot_instances(instance_ids=[ec2_id], dry_run=False)
|
||||
LOG.info("Soft Reboot Complete.")
|
||||
|
||||
def _hard_reboot(self, context, instance, network_info, block_device_info=None):
|
||||
def _hard_reboot(self, context, instance, network_info,
|
||||
block_device_info=None):
|
||||
self.power_off(instance)
|
||||
self.power_on(context, instance, network_info, block_device)
|
||||
LOG.info("Hard Reboot Complete.")
|
||||
@ -480,9 +485,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
return CONF.my_ip
|
||||
|
||||
def set_admin_password(self, instance, new_pass):
|
||||
"""
|
||||
Boto doesn't support setting the password at the time of creating an instance.
|
||||
hence not implemented.
|
||||
"""Boto doesn't support setting the password at the time of creating an
|
||||
instance, hence not implemented.
|
||||
"""
|
||||
pass
|
||||
|
||||
@ -519,14 +523,14 @@ class EC2Driver(driver.ComputeDriver):
|
||||
pass
|
||||
|
||||
def power_off(self, instance, timeout=0, retry_interval=0):
|
||||
"""
|
||||
Power off the specified instance.
|
||||
"""Power off the specified instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param timeout: time to wait for GuestOS to shutdown
|
||||
:param retry_interval: How often to signal guest while
|
||||
waiting for it to shutdown
|
||||
"""
|
||||
# TODO: Need to use timeout and retry_interval
|
||||
# TODO(timeout): Need to use timeout and retry_interval
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
self.ec2_conn.stop_instances(
|
||||
instance_ids=[ec2_id], force=False, dry_run=False)
|
||||
@ -546,38 +550,38 @@ class EC2Driver(driver.ComputeDriver):
|
||||
pass
|
||||
|
||||
def pause(self, instance):
|
||||
"""
|
||||
Boto doesn't support pause and cannot save system state and hence
|
||||
"""Boto doesn't support pause and cannot save system state and hence
|
||||
we've implemented the closest functionality which is to poweroff the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
self.power_off(instance)
|
||||
|
||||
def unpause(self, instance):
|
||||
"""
|
||||
Since Boto doesn't support pause and cannot save system state, we
|
||||
"""Since Boto doesn't support pause and cannot save system state, we
|
||||
had implemented the closest functionality which is to poweroff the
|
||||
instance. and powering on such an instance in this method.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
self.power_on(
|
||||
context=None, instance=instance, network_info=None, block_device_info=None)
|
||||
self.power_on(context=None, instance=instance, network_info=None,
|
||||
block_device_info=None)
|
||||
|
||||
def suspend(self, context, instance):
|
||||
"""
|
||||
Boto doesn't support suspend and cannot save system state and hence
|
||||
"""Boto doesn't support suspend and cannot save system state and hence
|
||||
we've implemented the closest functionality which is to poweroff the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
self.power_off(instance)
|
||||
|
||||
def resume(self, context, instance, network_info, block_device_info=None):
|
||||
"""
|
||||
Since Boto doesn't support suspend and we cannot save system state,
|
||||
"""Since Boto doesn't support suspend and we cannot save system state,
|
||||
we've implemented the closest functionality which is to power on the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
self.power_on(context, instance, network_info, block_device_info)
|
||||
@ -630,8 +634,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
|
||||
def attach_volume(self, context, connection_info, instance, mountpoint,
|
||||
disk_bus=None, device_type=None, encryption=None):
|
||||
"""Attach the disk to the instance at mountpoint using info.
|
||||
"""
|
||||
"""Attach the disk to the instance at mountpoint using info."""
|
||||
instance_name = instance['name']
|
||||
if instance_name not in self._mounts:
|
||||
self._mounts[instance_name] = {}
|
||||
@ -644,9 +647,9 @@ class EC2Driver(driver.ComputeDriver):
|
||||
self.ec2_conn.attach_volume(volume_id, ec2_id, mountpoint,
|
||||
dry_run=False)
|
||||
|
||||
def detach_volume(self, connection_info, instance, mountpoint, encryption=None):
|
||||
"""Detach the disk attached to the instance.
|
||||
"""
|
||||
def detach_volume(self, connection_info, instance, mountpoint,
|
||||
encryption=None):
|
||||
"""Detach the disk attached to the instance."""
|
||||
try:
|
||||
del self._mounts[instance['name']][mountpoint]
|
||||
except KeyError:
|
||||
@ -659,21 +662,20 @@ class EC2Driver(driver.ComputeDriver):
|
||||
|
||||
def swap_volume(self, old_connection_info, new_connection_info,
|
||||
instance, mountpoint, resize_to):
|
||||
"""Replace the disk attached to the instance.
|
||||
"""
|
||||
# TODO: Use resize_to parameter
|
||||
"""Replace the disk attached to the instance."""
|
||||
# TODO(resize_to): Use resize_to parameter
|
||||
instance_name = instance['name']
|
||||
if instance_name not in self._mounts:
|
||||
self._mounts[instance_name] = {}
|
||||
self._mounts[instance_name][mountpoint] = new_connection_info
|
||||
|
||||
old_volume_id = old_connection_info['data']['volume_id']
|
||||
new_volume_id = new_connection_info['data']['volume_id']
|
||||
|
||||
self.detach_volume(old_connection_info, instance, mountpoint)
|
||||
# wait for the old volume to detach successfully to make sure
|
||||
# /dev/sdn is available for the new volume to be attached
|
||||
# TODO: remove the sleep and poll AWS for the status of volume
|
||||
# TODO(remove_sleep): remove sleep and poll AWS for the status of
|
||||
# volume
|
||||
time.sleep(60)
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
self.ec2_conn.attach_volume(new_volume_id,
|
||||
@ -696,6 +698,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
|
||||
def get_info(self, instance):
|
||||
"""Get the current status of an instance, by name (not ID!)
|
||||
|
||||
:param instance: nova.objects.instance.Instance object
|
||||
Returns a dict containing:
|
||||
:state: the running state, one of the power_state codes
|
||||
@ -725,13 +728,9 @@ class EC2Driver(driver.ComputeDriver):
|
||||
memory_mb = ec2_flavor['memory_mb']
|
||||
vcpus = ec2_flavor['vcpus']
|
||||
|
||||
return hardware.InstanceInfo(
|
||||
state=power_state,
|
||||
max_mem_kb=memory_mb,
|
||||
mem_kb=memory_mb,
|
||||
num_cpu=vcpus,
|
||||
cpu_time_ns=0,
|
||||
id=instance.id)
|
||||
return hardware.InstanceInfo(state=power_state, max_mem_kb=memory_mb,
|
||||
mem_kb=memory_mb, num_cpu=vcpus,
|
||||
cpu_time_ns=0, id=instance.id)
|
||||
|
||||
def allow_key(self, key):
|
||||
for key_to_filter in DIAGNOSTIC_KEYS_TO_FILTER:
|
||||
@ -743,9 +742,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
"""Return data about VM diagnostics."""
|
||||
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
ec2_instances = self.ec2_conn.get_only_instances(instance_ids=[ec2_id],
|
||||
filters=None,
|
||||
dry_run=False,
|
||||
ec2_instances = self.ec2_conn.get_only_instances(
|
||||
instance_ids=[ec2_id], filters=None, dry_run=False,
|
||||
max_results=None)
|
||||
if len(ec2_instances) == 0:
|
||||
LOG.warning(_("EC2 instance with ID %s not found") % ec2_id,
|
||||
@ -758,7 +756,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
if self.allow_key(key):
|
||||
diagnostics['instance.' + key] = str(value)
|
||||
|
||||
metrics = self.cloudwatch_conn.list_metrics(dimensions={'InstanceId': ec2_id})
|
||||
metrics = self.cloudwatch_conn.list_metrics(
|
||||
dimensions={'InstanceId': ec2_id})
|
||||
|
||||
for metric in metrics:
|
||||
end = datetime.datetime.utcnow()
|
||||
@ -766,7 +765,6 @@ class EC2Driver(driver.ComputeDriver):
|
||||
details = metric.query(start, end, 'Average', None, 3600)
|
||||
if len(details) > 0:
|
||||
diagnostics['metrics.' + str(metric)] = details[0]
|
||||
|
||||
return diagnostics
|
||||
|
||||
def get_all_bw_counters(self, instances):
|
||||
@ -804,7 +802,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
if instance.ip_address is not None:
|
||||
host_ip = instance.ip_address
|
||||
if host_ip is not None:
|
||||
LOG.info("Found the IP of the instance IP:%s and port:%s" % (host_ip, vnc_port))
|
||||
LOG.info("Found the IP of the instance IP:%s and port:%s" % (
|
||||
host_ip, vnc_port))
|
||||
return ctype.ConsoleVNC(host=host_ip, port=vnc_port)
|
||||
else:
|
||||
LOG.info("Ip not Found for the instance")
|
||||
@ -816,7 +815,6 @@ class EC2Driver(driver.ComputeDriver):
|
||||
"""Simple Protocol for Independent Computing Environments
|
||||
Doesn't seem to be supported by AWS EC2 directly
|
||||
"""
|
||||
|
||||
return {'internal_access_path': 'EC2',
|
||||
'host': 'EC2spiceconsole.com',
|
||||
'port': 6969,
|
||||
@ -833,13 +831,17 @@ class EC2Driver(driver.ComputeDriver):
|
||||
def get_available_resource(self, nodename):
|
||||
"""Retrieve resource information.
|
||||
Updates compute manager resource info on ComputeNode table.
|
||||
This method is called when nova-compute launches and as part of a periodic task that records results in the DB.
|
||||
Since we don't have a real hypervisor, pretend we have lots of disk and ram.
|
||||
This method is called when nova-compute launches and as part of a
|
||||
periodic task that records results in the DB.
|
||||
Since we don't have a real hypervisor, pretend we have lots of disk and
|
||||
ram.
|
||||
|
||||
:param nodename:
|
||||
node which the caller want to get resources from
|
||||
a driver that manages only one node can safely ignore this
|
||||
:returns: Dictionary describing resources
|
||||
"""
|
||||
global _EC2_NODES
|
||||
if nodename not in _EC2_NODES:
|
||||
return {}
|
||||
|
||||
@ -891,29 +893,35 @@ class EC2Driver(driver.ComputeDriver):
|
||||
network_info, image_meta, resize_instance,
|
||||
block_device_info=None, power_on=True):
|
||||
"""Completes a resize
|
||||
|
||||
:param migration: the migrate/resize information
|
||||
:param instance: nova.objects.instance.Instance being migrated/resized
|
||||
:param power_on: is True the instance should be powered on
|
||||
"""
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
ec_instance_info = self.ec2_conn.get_only_instances(
|
||||
instance_ids=[ec2_id], filters=None, dry_run=False, max_results=None)
|
||||
instance_ids=[ec2_id], filters=None, dry_run=False,
|
||||
max_results=None)
|
||||
ec2_instance = ec_instance_info[0]
|
||||
|
||||
# EC2 instance needs to be stopped to modify it's attribute. So we stop the instance,
|
||||
# modify the instance type in this case, and then restart the instance.
|
||||
# EC2 instance needs to be stopped to modify it's attribute. So we stop
|
||||
# the instance, modify the instance type in this case, and then restart
|
||||
# the instance.
|
||||
ec2_instance.stop()
|
||||
self._wait_for_state(instance, ec2_id, "stopped", power_state.SHUTDOWN)
|
||||
new_instance_type = flavor_map[migration['new_instance_type_id']]
|
||||
# TODO(flavor_map is undefined): need to check flavor type variable
|
||||
new_instance_type = flavor_map[migration['new_instance_type_id']] # noqa
|
||||
ec2_instance.modify_attribute('instanceType', new_instance_type)
|
||||
|
||||
def confirm_migration(self, migration, instance, network_info):
|
||||
"""Confirms a resize, destroying the source VM.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
ec2_id = self._get_ec2_id_from_instance(instance)
|
||||
ec_instance_info = self.ec2_conn.get_only_instances(
|
||||
instance_ids=[ec2_id], filters=None, dry_run=False, max_results=None)
|
||||
instance_ids=[ec2_id], filters=None, dry_run=False,
|
||||
max_results=None)
|
||||
ec2_instance = ec_instance_info[0]
|
||||
ec2_instance.start()
|
||||
self._wait_for_state(instance, ec2_id, "running", power_state.RUNNING)
|
||||
@ -928,6 +936,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
def get_host_stats(self, refresh=False):
|
||||
"""Return EC2 Host Status of name, ram, disk, network."""
|
||||
stats = []
|
||||
global _EC2_NODES
|
||||
for nodename in _EC2_NODES:
|
||||
host_status = self.host_status_base.copy()
|
||||
host_status['hypervisor_hostname'] = nodename
|
||||
@ -976,6 +985,7 @@ class EC2Driver(driver.ComputeDriver):
|
||||
return {'ip': '127.0.0.1', 'initiator': 'EC2', 'host': 'EC2host'}
|
||||
|
||||
def get_available_nodes(self, refresh=False):
|
||||
global _EC2_NODES
|
||||
return _EC2_NODES
|
||||
|
||||
def instance_on_disk(self, instance):
|
||||
@ -997,8 +1007,8 @@ class EC2Driver(driver.ComputeDriver):
|
||||
continue
|
||||
if len(instance.tags) > 0:
|
||||
if 'openstack_id' in instance.tags:
|
||||
self._uuid_to_ec2_instance[instance.tags['openstack_id']] = \
|
||||
instance
|
||||
self._uuid_to_ec2_instance[
|
||||
instance.tags['openstack_id']] = instance
|
||||
else:
|
||||
# Possibly a new discovered instance
|
||||
generate_uuid = True
|
||||
@ -1010,15 +1020,19 @@ class EC2Driver(driver.ComputeDriver):
|
||||
self._uuid_to_ec2_instance[instance_uuid] = instance
|
||||
return self._uuid_to_ec2_instance.keys()
|
||||
|
||||
def _wait_for_state(self, instance, ec2_id, desired_state, desired_power_state):
|
||||
"""Wait for the state of the corrosponding ec2 instance to be in completely available state.
|
||||
def _wait_for_state(self, instance, ec2_id, desired_state,
|
||||
desired_power_state):
|
||||
"""Wait for the state of the corrosponding ec2 instance to be in
|
||||
completely available state.
|
||||
|
||||
:params:ec2_id: the instance's corrosponding ec2 id.
|
||||
:params:desired_state: the desired state of the instance to be in.
|
||||
"""
|
||||
def _wait_for_power_state():
|
||||
"""Called at an interval until the VM is running again.
|
||||
"""
|
||||
ec2_instance = self.ec2_conn.get_only_instances(instance_ids=[ec2_id])
|
||||
ec2_instance = self.ec2_conn.get_only_instances(
|
||||
instance_ids=[ec2_id])
|
||||
|
||||
state = ec2_instance[0].state
|
||||
if state == desired_state:
|
||||
@ -1026,12 +1040,15 @@ class EC2Driver(driver.ComputeDriver):
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
def _wait_for_status_check():
|
||||
"""Power state of a machine might be ON, but status check is the one which gives the real
|
||||
"""Power state of a machine might be ON, but status check is the
|
||||
one which gives the real
|
||||
"""
|
||||
ec2_instance = self.ec2_conn.get_all_instance_status(instance_ids=[ec2_id])[0]
|
||||
ec2_instance = self.ec2_conn.get_all_instance_status(
|
||||
instance_ids=[ec2_id])[0]
|
||||
if ec2_instance.system_status.status == 'ok':
|
||||
LOG.info("Instance status check is %s / %s" %
|
||||
(ec2_instance.system_status.status, ec2_instance.instance_status.status))
|
||||
(ec2_instance.system_status.status,
|
||||
ec2_instance.instance_status.status))
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
# waiting for the power state to change
|
||||
@ -1040,13 +1057,15 @@ class EC2Driver(driver.ComputeDriver):
|
||||
|
||||
def _wait_for_image_state(self, ami_id, desired_state):
|
||||
"""Timer to wait for the image/snapshot to reach a desired state
|
||||
|
||||
:params:ami_id: correspoding image id in Amazon
|
||||
:params:desired_state: the desired new state of the image to be in.
|
||||
"""
|
||||
def _wait_for_state():
|
||||
"""Called at an interval until the AMI image is available."""
|
||||
try:
|
||||
images = self.ec2_conn.get_all_images(image_ids=[ami_id], owners=None,
|
||||
images = self.ec2_conn.get_all_images(
|
||||
image_ids=[ami_id], owners=None,
|
||||
executable_by=None, filters=None, dry_run=None)
|
||||
state = images[0].state
|
||||
if state == desired_state:
|
||||
|
@ -1,17 +1,30 @@
|
||||
"""
|
||||
Copyright (c) 2014 Thoughtworks.
|
||||
Copyright (c) 2016 Platform9 Systems 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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from nova import exception
|
||||
|
||||
class Ec2ExceptionHandler:
|
||||
"""
|
||||
This is a class which can be used to create mapping between EC2 Exception messages to Nova based Exceptions.
|
||||
Also gives control on the error message displayed to the user.
|
||||
|
||||
class Ec2ExceptionHandler(object):
|
||||
"""This is a class which can be used to create mapping between EC2
|
||||
Exception messages to Nova based Exceptions. Also gives control on the
|
||||
error message displayed to the user.
|
||||
"""
|
||||
@staticmethod
|
||||
def get_processed_exception(ec2_response_error_exc):
|
||||
if ec2_response_error_exc.error_code == "AuthFailure":
|
||||
return exception.Forbidden("Please check AWS credentials")
|
||||
elif ec2_response_error_exc.error_code == "InvalidAMIID.NotFound":
|
||||
if ec2_response_error_exc.error_code == "InvalidAMIID.NotFound":
|
||||
return exception.ImageNotFoundEC2("Invalid Image")
|
||||
else:
|
||||
return exception.NovaException(ec2_response_error_exc.message)
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from nova.virt.gce import driver
|
||||
|
||||
|
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
from nova.compute import power_state
|
||||
|
||||
|
@ -1,27 +1,23 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
#
|
||||
# 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.
|
||||
'''
|
||||
1. Source openstack RC file
|
||||
2. python create-nova-flavors-gce.py <service_key_path> <project> <zone>
|
||||
'''
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc. (http://www.platform9.com)
|
||||
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 os
|
||||
import sys
|
||||
import gceutils
|
||||
|
||||
from gce import gceutils
|
||||
from keystoneauth1 import loading
|
||||
from keystoneauth1 import session
|
||||
from novaclient import client as nova_client
|
||||
from keystoneauth1 import loading, session
|
||||
|
||||
|
||||
def get_env_param(env_name):
|
||||
@ -42,7 +38,8 @@ def get_keystone_session(
|
||||
|
||||
if not project_name:
|
||||
if not tenant_name:
|
||||
raise Exception("Either OS_PROJECT_NAME or OS_TENANT_NAME is required.")
|
||||
raise Exception("Either OS_PROJECT_NAME or OS_TENANT_NAME is "
|
||||
"required.")
|
||||
project_name = tenant_name
|
||||
|
||||
loader = loading.get_plugin_loader('password')
|
||||
@ -78,7 +75,8 @@ class GceFlavors(object):
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 4:
|
||||
print('Usage: {0} <service_key_path> <project> <zone>'.format(sys.argv[0]))
|
||||
print('Usage: {0} <service_key_path> <project> <zone>'.format(
|
||||
sys.argv[0]))
|
||||
sys.exit(1)
|
||||
gce_flavors = GceFlavors(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
gce_flavors.register_gce_flavors()
|
||||
|
@ -1,36 +1,36 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Connection to the Google Cloud Platform - GCE service"""
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import gceutils
|
||||
import hashlib
|
||||
import uuid
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from googleapiclient.errors import HttpError
|
||||
from nova.compute import task_states
|
||||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova.virt import driver, hardware
|
||||
from nova.virt import driver
|
||||
from nova.virt.gce.constants import GCE_STATE_MAP
|
||||
from nova.virt import hardware
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from nova.compute import task_states
|
||||
|
||||
import gceutils
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
from nova.virt.gce.constants import GCE_STATE_MAP
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
_GCE_NODES = None
|
||||
|
||||
gce_group = cfg.OptGroup(name='GCE',
|
||||
title='Options to connect to Google cloud')
|
||||
|
||||
@ -61,7 +61,6 @@ DIAGNOSTIC_KEYS_TO_FILTER = ['group', 'block_device_mapping']
|
||||
|
||||
def set_nodes(nodes):
|
||||
"""Sets GCE Driver's node.list.
|
||||
|
||||
It has effect on the following methods:
|
||||
get_available_nodes()
|
||||
get_available_resource
|
||||
@ -75,7 +74,6 @@ def set_nodes(nodes):
|
||||
|
||||
def restore_nodes():
|
||||
"""Resets GCE Driver's node list modified by set_nodes().
|
||||
|
||||
Usually called from tearDown().
|
||||
"""
|
||||
global _GCE_NODES
|
||||
@ -112,15 +110,14 @@ class GCEDriver(driver.ComputeDriver):
|
||||
self.gce_svc_key = CONF.GCE.service_key_path
|
||||
|
||||
def init_host(self, host):
|
||||
"""
|
||||
Initialize anything that is necessary for the driver to function
|
||||
"""
|
||||
"""Initialize anything that is necessary for the driver to function"""
|
||||
global _GCE_NODES
|
||||
self.gce_svc = gceutils.get_gce_service(self.gce_svc_key)
|
||||
self.gce_flavor_info = gceutils.get_machines_info(
|
||||
self.gce_svc, self.gce_project, self.gce_zone)
|
||||
LOG.info("GCE driver init with %s project, %s region" %
|
||||
(self.gce_project, self.gce_zone))
|
||||
if '_GCE_NODES' not in globals():
|
||||
if _GCE_NODES is None:
|
||||
set_nodes([CONF.host])
|
||||
|
||||
def _get_uuid_from_gce_id(self, gce_id):
|
||||
@ -208,11 +205,9 @@ class GCEDriver(driver.ComputeDriver):
|
||||
admin_password, network_info=None, block_device_info=None):
|
||||
"""Create a new instance/VM/domain on the virtualization platform.
|
||||
Once this successfully completes, the instance should be
|
||||
running (power_state.RUNNING).
|
||||
|
||||
If this fails, any partial instance should be completely
|
||||
cleaned up, and the virtualization platform should be in the state
|
||||
that it was before this call began.
|
||||
running (power_state.RUNNING). If this fails, any partial instance
|
||||
should be completely cleaned up, and the virtualization platform should
|
||||
be in the state that it was before this call began.
|
||||
|
||||
:param context: security context <Not Yet Implemented>
|
||||
:param instance: nova.objects.instance.Instance
|
||||
@ -228,7 +223,8 @@ class GCEDriver(driver.ComputeDriver):
|
||||
attached to the instance.
|
||||
"""
|
||||
compute, project, zone = self.gce_svc, self.gce_project, self.gce_zone
|
||||
# GCE expects instance name in format "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
|
||||
# GCE expects instance name in format
|
||||
# "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
|
||||
# So we need to construct it for GCE from uuid
|
||||
gce_instance_name = 'inst-' + instance.uuid
|
||||
LOG.info("Creating instance %s as %s on GCE." %
|
||||
@ -267,6 +263,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
"""Snapshot an image of the specified instance
|
||||
|
||||
:param context: security context
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param image_id: Reference to a pre-created image holding the snapshot.
|
||||
@ -296,7 +293,8 @@ class GCEDriver(driver.ComputeDriver):
|
||||
boot_disk = gceutils.get_instance_boot_disk(
|
||||
compute, project, zone, gce_id)
|
||||
except AssertionError:
|
||||
reason = "Unable to find boot disk from instance metadata %s" % instance.uuid
|
||||
reason = "Unable to find boot disk from instance metadata {0}"
|
||||
reason = reason.format(instance.uuid)
|
||||
raise exception.InvalidMetadata(reason=reason)
|
||||
disk_name = boot_disk['name']
|
||||
LOG.debug("1. Found boot disk %s for instance %s" %
|
||||
@ -408,12 +406,10 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def reboot(self, context, instance, network_info, reboot_type,
|
||||
block_device_info=None, bad_volumes_callback=None):
|
||||
"""
|
||||
Reboot the specified instance.
|
||||
After this is called successfully, the instance's state
|
||||
goes back to power_state.RUNNING. The virtualization
|
||||
platform should ensure that the reboot action has completed
|
||||
successfully even in cases in which the underlying domain/vm
|
||||
"""Reboot the specified instance. After this is called successfully,
|
||||
the instance's state goes back to power_state.RUNNING. The
|
||||
virtualization platform should ensure that the reboot action has
|
||||
completed successfully even in cases in which the underlying domain/vm
|
||||
is paused or halted/stopped.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
@ -493,8 +489,8 @@ class GCEDriver(driver.ComputeDriver):
|
||||
raise NotImplementedError()
|
||||
|
||||
def power_off(self, instance, timeout=0, retry_interval=0):
|
||||
"""
|
||||
Power off the specified instance.
|
||||
"""Power off the specified instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param timeout: time to wait for GuestOS to shutdown
|
||||
:param retry_interval: How often to signal guest while
|
||||
@ -529,6 +525,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
GCE doesn't support pause and cannot save system state and hence
|
||||
we've implemented the closest functionality which is to poweroff the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
LOG.info("Pause instance %s" % instance.uuid)
|
||||
@ -539,6 +536,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
Since GCE doesn't support pause and cannot save system state, we
|
||||
had implemented the closest functionality which is to poweroff the
|
||||
instance. and powering on such an instance in this method.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
LOG.info("Unpause instance %s" % instance.uuid)
|
||||
@ -550,6 +548,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
GCE doesn't support suspend and cannot save system state and hence
|
||||
we've implemented the closest functionality which is to poweroff the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
LOG.info("Suspending instance %s" % instance.uuid)
|
||||
@ -560,6 +559,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
Since GCE doesn't support resume and we cannot save system state,
|
||||
we've implemented the closest functionality which is to power on the
|
||||
instance.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
LOG.info("Resuming instance %s" % instance.uuid)
|
||||
@ -568,7 +568,6 @@ class GCEDriver(driver.ComputeDriver):
|
||||
def destroy(self, context, instance, network_info, block_device_info=None,
|
||||
destroy_disks=True, migrate_data=None):
|
||||
"""Destroy the specified instance from the Hypervisor.
|
||||
|
||||
If the instance is not found (for example if networking failed), this
|
||||
function should still succeed. It's probably a good idea to log a
|
||||
warning in that case.
|
||||
@ -596,16 +595,15 @@ class GCEDriver(driver.ComputeDriver):
|
||||
except HttpError:
|
||||
# Sometimes instance may not exist in GCE, in that case we just
|
||||
# allow deleting VM from openstack
|
||||
LOG.error("Instance %s not found in GCE, removing from openstack." %
|
||||
instance.uuid)
|
||||
LOG.error("Instance {0} not found in GCE, removing from openstack."
|
||||
.format(instance.uuid))
|
||||
return
|
||||
gceutils.wait_for_operation(compute, project, operation)
|
||||
LOG.info("Destroy Complete %s" % instance.uuid)
|
||||
|
||||
def attach_volume(self, context, connection_info, instance, mountpoint,
|
||||
disk_bus=None, device_type=None, encryption=None):
|
||||
"""Attach the disk to the instance at mountpoint using info.
|
||||
"""
|
||||
"""Attach the disk to the instance at mountpoint using info."""
|
||||
compute, project, zone = self.gce_svc, self.gce_project, self.gce_zone
|
||||
gce_id = self._get_gce_id_from_instance(instance)
|
||||
gce_volume = connection_info['data']
|
||||
@ -619,8 +617,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def detach_volume(self, connection_info, instance, mountpoint,
|
||||
encryption=None):
|
||||
"""Detach the disk attached to the instance.
|
||||
"""
|
||||
"""Detach the disk attached to the instance."""
|
||||
compute, project, zone = self.gce_svc, self.gce_project, self.gce_zone
|
||||
gce_id = self._get_gce_id_from_instance(instance)
|
||||
gce_volume = connection_info['data']
|
||||
@ -633,8 +630,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def swap_volume(self, old_connection_info, new_connection_info, instance,
|
||||
mountpoint, resize_to):
|
||||
"""Replace the disk attached to the instance.
|
||||
"""
|
||||
"""Replace the disk attached to the instance."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def attach_interface(self, instance, image_meta, vif):
|
||||
@ -645,6 +641,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def get_info(self, instance):
|
||||
"""Get the current status of an instance, by name (not ID!)
|
||||
|
||||
:param instance: nova.objects.instance.Instance object
|
||||
Returns a dict containing:
|
||||
:state: the running state, one of the power_state codes
|
||||
@ -697,13 +694,12 @@ class GCEDriver(driver.ComputeDriver):
|
||||
"""Return bandwidth usage counters for each interface on each
|
||||
running VM.
|
||||
"""
|
||||
|
||||
bw = []
|
||||
return bw
|
||||
|
||||
def get_all_volume_usage(self, context, compute_host_bdms):
|
||||
"""Return usage info for volumes attached to vms on
|
||||
a given host.
|
||||
"""
|
||||
"""Return usage info for volumes attached to vms on a given host."""
|
||||
volusage = []
|
||||
return volusage
|
||||
|
||||
@ -717,8 +713,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_spice_console(self, instance):
|
||||
""" Simple Protocol for Independent Computing Environments
|
||||
"""
|
||||
"""Simple Protocol for Independent Computing Environments"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_console_pool_info(self, console_type):
|
||||
@ -728,16 +723,17 @@ class GCEDriver(driver.ComputeDriver):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_available_resource(self, nodename):
|
||||
"""Retrieve resource information.
|
||||
Updates compute manager resource info on ComputeNode table.
|
||||
This method is called when nova-compute launches and as part of
|
||||
a periodic task that records results in the DB.
|
||||
Without real hypervisor, pretend we have lots of disk and ram.
|
||||
:param nodename:
|
||||
node which the caller want to get resources from
|
||||
"""Retrieve resource information. Updates compute manager resource info
|
||||
on ComputeNode table. This method is called when nova-compute launches
|
||||
and as part of a periodic task that records results in the DB. Without
|
||||
real hypervisor, pretend we have lots of disk and ram.
|
||||
|
||||
:param nodename: node which the caller want to get resources from
|
||||
a driver that manages only one node can safely ignore this
|
||||
:returns: Dictionary describing resources
|
||||
"""
|
||||
|
||||
global _GCE_NODES
|
||||
if nodename not in _GCE_NODES:
|
||||
return {}
|
||||
|
||||
@ -789,6 +785,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
network_info, image_meta, resize_instance,
|
||||
block_device_info=None, power_on=True):
|
||||
"""Completes a resize
|
||||
|
||||
:param migration: the migrate/resize information
|
||||
:param instance: nova.objects.instance.Instance being migrated/resized
|
||||
:param power_on: is True the instance should be powered on
|
||||
@ -797,6 +794,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def confirm_migration(self, migration, instance, network_info):
|
||||
"""Confirms a resize, destroying the source VM.
|
||||
|
||||
:param instance: nova.objects.instance.Instance
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
@ -810,6 +808,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
|
||||
def get_host_stats(self, refresh=False):
|
||||
"""Return GCE Host Status of name, ram, disk, network."""
|
||||
global _GCE_NODES
|
||||
stats = []
|
||||
for nodename in _GCE_NODES:
|
||||
host_status = self.host_status_base.copy()
|
||||
@ -836,6 +835,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
"""Start/Stop host maintenance window. On start, it triggers
|
||||
guest VMs evacuation.
|
||||
"""
|
||||
|
||||
if not mode:
|
||||
return 'off_maintenance'
|
||||
return 'on_maintenance'
|
||||
@ -859,6 +859,7 @@ class GCEDriver(driver.ComputeDriver):
|
||||
return {'ip': '127.0.0.1', 'initiator': 'GCE', 'host': 'GCEhost'}
|
||||
|
||||
def get_available_nodes(self, refresh=False):
|
||||
global _GCE_NODES
|
||||
return _GCE_NODES
|
||||
|
||||
def instance_on_disk(self, instance):
|
||||
|
@ -1,24 +1,23 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import time
|
||||
import six
|
||||
from oslo_log import log as logging
|
||||
import time
|
||||
|
||||
from nova.i18n import _
|
||||
from googleapiclient.discovery import build
|
||||
from nova.i18n import _
|
||||
from oauth2client.client import GoogleCredentials
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
from oslo_utils import reflection
|
||||
from six.moves import urllib
|
||||
@ -72,6 +71,7 @@ class GceOperationError(Exception):
|
||||
|
||||
def list_instances(compute, project, zone):
|
||||
"""Returns list of GCE instance resources for specified project
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -84,6 +84,7 @@ def list_instances(compute, project, zone):
|
||||
|
||||
def get_instance(compute, project, zone, instance):
|
||||
"""Get GCE instance information
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -96,6 +97,7 @@ def get_instance(compute, project, zone, instance):
|
||||
|
||||
def get_instance_metadata(compute, project, zone, instance):
|
||||
"""Returns specified instance's metadata
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -109,6 +111,7 @@ def get_instance_metadata(compute, project, zone, instance):
|
||||
|
||||
def get_instances_metadata_key(compute, project, zone, instance, key):
|
||||
"""Returns particular key information for specified instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -126,6 +129,7 @@ def get_instances_metadata_key(compute, project, zone, instance, key):
|
||||
|
||||
def get_external_ip(compute, project, zone, instance):
|
||||
"""Return external IP of GCE instance return empty string otherwise
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -144,6 +148,7 @@ def get_external_ip(compute, project, zone, instance):
|
||||
def set_instance_metadata(compute, project, zone, instance, items,
|
||||
operation='add'):
|
||||
"""Perform specified operation on GCE instance metadata
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -165,7 +170,7 @@ def set_instance_metadata(compute, project, zone, instance, items,
|
||||
else:
|
||||
metadata['items'] = items
|
||||
LOG.info("Adding metadata %s" % (metadata))
|
||||
# TODO: Add del operation if required
|
||||
# TODO(del_operation): Add del operation if required
|
||||
return compute.instances().setMetadata(project=project, zone=zone,
|
||||
instance=instance,
|
||||
body=metadata).execute()
|
||||
@ -174,6 +179,7 @@ def set_instance_metadata(compute, project, zone, instance, items,
|
||||
def create_instance(compute, project, zone, name, image_link, machine_link,
|
||||
network_interfaces):
|
||||
"""Create GCE instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -214,6 +220,7 @@ def create_instance(compute, project, zone, name, image_link, machine_link,
|
||||
|
||||
def delete_instance(compute, project, zone, name):
|
||||
"""Delete GCE instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -225,6 +232,7 @@ def delete_instance(compute, project, zone, name):
|
||||
|
||||
def stop_instance(compute, project, zone, name):
|
||||
"""Stop GCE instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -236,6 +244,7 @@ def stop_instance(compute, project, zone, name):
|
||||
|
||||
def start_instance(compute, project, zone, name):
|
||||
"""Start GCE instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -247,6 +256,7 @@ def start_instance(compute, project, zone, name):
|
||||
|
||||
def reset_instance(compute, project, zone, name):
|
||||
"""Hard reset GCE instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -258,10 +268,12 @@ def reset_instance(compute, project, zone, name):
|
||||
|
||||
def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
"""Wait for GCE operation to complete, raise error if operation failure
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param operation: object, Operation resource obtained by calling GCE asynchronous API
|
||||
All GCE asynchronous API's return operation resource to followup there completion.
|
||||
:param operation: object, Operation resource obtained by calling GCE
|
||||
asynchronous API. All GCE asynchronous API's return operation resource to
|
||||
followup there completion.
|
||||
:param interval: int, Time period(seconds) between two GCE operation checks
|
||||
:param timeout: int, Absoulte time period(seconds) to monitor GCE operation
|
||||
"""
|
||||
@ -295,6 +307,7 @@ def wait_for_operation(compute, project, operation, interval=1, timeout=60):
|
||||
|
||||
def get_gce_service(service_key):
|
||||
"""Returns GCE compute resource object for interacting with GCE API
|
||||
|
||||
:param service_key: string, Path of service key obtained from
|
||||
https://console.cloud.google.com/apis/credentials
|
||||
"""
|
||||
@ -305,6 +318,7 @@ def get_gce_service(service_key):
|
||||
|
||||
def get_machines_info(compute, project, zone):
|
||||
"""Return machine type info from GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -323,6 +337,7 @@ def get_machines_info(compute, project, zone):
|
||||
|
||||
def get_images(compute, project):
|
||||
"""Return public images info from GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
"""
|
||||
@ -336,6 +351,7 @@ def get_images(compute, project):
|
||||
|
||||
def get_image(compute, project, name):
|
||||
"""Return public images info from GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
"""
|
||||
@ -345,6 +361,7 @@ def get_image(compute, project, name):
|
||||
|
||||
def delete_image(compute, project, name):
|
||||
"""Delete image from GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE image name
|
||||
@ -357,6 +374,7 @@ def delete_image(compute, project, name):
|
||||
|
||||
def get_network(compute, project, name):
|
||||
"""Return network info
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE network name
|
||||
@ -367,6 +385,7 @@ def get_network(compute, project, name):
|
||||
|
||||
def attach_disk(compute, project, zone, instance_name, disk_name, disk_link):
|
||||
"""Attach disk to instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -392,6 +411,7 @@ def attach_disk(compute, project, zone, instance_name, disk_name, disk_link):
|
||||
|
||||
def detach_disk(compute, project, zone, instance_name, disk_name):
|
||||
"""Detach disk from instance
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -406,8 +426,7 @@ def detach_disk(compute, project, zone, instance_name, disk_name):
|
||||
|
||||
|
||||
def get_instance_boot_disk(compute, project, zone, instance):
|
||||
"""Return boot disk info for instance
|
||||
"""
|
||||
"""Return boot disk info for instance"""
|
||||
gce_instance = get_instance(compute, project, zone, instance)
|
||||
for disk in gce_instance['disks']:
|
||||
if disk['boot']:
|
||||
@ -426,6 +445,7 @@ def get_instance_boot_disk(compute, project, zone, instance):
|
||||
|
||||
def create_disk(compute, project, zone, name, size):
|
||||
"""Create disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -446,6 +466,7 @@ def create_disk(compute, project, zone, name, size):
|
||||
|
||||
def delete_disk(compute, project, zone, name):
|
||||
"""Delete disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -459,6 +480,7 @@ def delete_disk(compute, project, zone, name):
|
||||
|
||||
def get_disk(compute, project, zone, name):
|
||||
"""Get info of disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -471,6 +493,7 @@ def get_disk(compute, project, zone, name):
|
||||
|
||||
def snapshot_disk(compute, project, zone, name, snapshot_name):
|
||||
"""Create snapshot of disk in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
@ -486,6 +509,7 @@ def snapshot_disk(compute, project, zone, name, snapshot_name):
|
||||
|
||||
def get_snapshot(compute, project, name):
|
||||
"""Get info of snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE snapshot name
|
||||
@ -497,6 +521,7 @@ def get_snapshot(compute, project, name):
|
||||
|
||||
def delete_snapshot(compute, project, name):
|
||||
"""Delete snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param name: string, GCE snapshot name
|
||||
@ -509,6 +534,7 @@ def delete_snapshot(compute, project, name):
|
||||
def create_disk_from_snapshot(compute, project, zone, name, snapshot_name,
|
||||
disk_type="pd-standard"):
|
||||
"""Create disk from snapshot in GCE
|
||||
|
||||
:param compute: GCE compute resource object using googleapiclient.discovery
|
||||
:param project: string, GCE Project Id
|
||||
:param zone: string, GCE Name of zone
|
||||
|
@ -95,7 +95,6 @@ check_results glance_store
|
||||
check_results neutron
|
||||
|
||||
echo "==========================================================================================="
|
||||
echo "Log files are in $DIRECTORY/. Please check log files for UNKNOWN status."
|
||||
echo "Cinder results: ${results[cinder]}"
|
||||
echo "Nova results: ${results[nova]}"
|
||||
echo "Glance results: ${results[glance_store]}"
|
||||
|
25
setup.py
25
setup.py
@ -1,16 +1,15 @@
|
||||
# Copyright (c) 2017 Platform9 Systems Inc.
|
||||
#
|
||||
# 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 expressed or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
|
||||
import setuptools
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
hacking>=0.12.0,<0.13 # Apache-2.0
|
||||
zuul
|
15
tox.ini
15
tox.ini
@ -1,25 +1,24 @@
|
||||
[tox]
|
||||
envlist = py27,pep8
|
||||
envlist = pep8,py27
|
||||
skipsdist = True
|
||||
minversion = 2.3.2
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
whitelist_externals = bash
|
||||
[testenv:py27]
|
||||
deps = zuul
|
||||
commands =
|
||||
zuul-cloner --cache-dir /opt/git git://git.openstack.org \
|
||||
openstack/nova openstack/cinder \
|
||||
openstack/neutron openstack/glance_store
|
||||
bash run_tests.sh -wj
|
||||
./run_tests.sh -wj
|
||||
|
||||
[testenv:pep8]
|
||||
deps = hacking>=0.12.0,<0.13
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
show-source = True
|
||||
enable-extensions = H106,H203
|
||||
ignore = E123,E125
|
||||
ignore = E123,E125,H404,H405
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,openstack
|
||||
|
Loading…
Reference in New Issue
Block a user