From 264939cfb5b044e42286300c429cb45ab7d6bf70 Mon Sep 17 00:00:00 2001 From: Pratik Shah Date: Fri, 21 Jul 2017 17:27:33 +0530 Subject: [PATCH] Applied pep8 fixes Closes-Bug: #1703514 Change-Id: I89c25bd9e678dc2f7cc27f952aa4833e26c90f4a --- .../tests/unit/volume/drivers/gce/gce_mock.py | 28 +- .../tests/unit/volume/drivers/gce/test_gce.py | 31 +- cinder/tests/unit/volume/drivers/test_ec2.py | 17 +- cinder/volume/drivers/aws/ebs.py | 90 +++--- cinder/volume/drivers/aws/exception.py | 1 + cinder/volume/drivers/gce/gceutils.py | 34 +- glance/aws/create-glance-images-aws.py | 135 ++++---- glance/gce/create-glance-images-gce.py | 36 +-- glance/gce/gceutils.py | 28 +- glance/glance_store/_drivers/aws.py | 52 ++- glance/glance_store/_drivers/gce.py | 32 +- .../glance_store/tests/unit/gce/gce_mock.py | 25 +- .../glance_store/tests/unit/gce/test_gce.py | 33 +- neutron/neutron/common/aws_utils.py | 148 +++++---- neutron/neutron/common/gceutils.py | 61 ++-- .../plugins/ml2/drivers/aws/callbacks.py | 38 ++- .../plugins/ml2/drivers/aws/mechanism_aws.py | 120 +++---- .../plugins/ml2/drivers/gce/mech_gce.py | 41 +-- .../services/l3_router/aws_router_plugin.py | 194 +++++++----- .../services/l3_router/gce_router_plugin.py | 47 +-- neutron/tests/common/gce/gce_mock.py | 25 +- .../tests/plugins/ml2/drivers/gce/test_gce.py | 38 +-- nova/tests/unit/virt/ec2/test_ec2.py | 56 ++-- nova/tests/unit/virt/gce/gce_mock.py | 25 +- nova/tests/unit/virt/gce/test_gce.py | 31 +- nova/virt/ec2/__init__.py | 27 +- nova/virt/ec2/cloud_burst_filter.py | 53 ++-- nova/virt/ec2/ec2driver.py | 299 ++++++++++-------- nova/virt/ec2/exception_handler.py | 27 +- nova/virt/gce/__init__.py | 25 +- nova/virt/gce/constants.py | 25 +- nova/virt/gce/create-nova-flavors-gce.py | 40 ++- nova/virt/gce/driver.py | 129 ++++---- nova/virt/gce/gceutils.py | 72 +++-- run_tests.sh | 1 - setup.py | 25 +- test-requirements.txt | 2 - tox.ini | 15 +- 38 files changed, 1122 insertions(+), 984 deletions(-) delete mode 100644 test-requirements.txt diff --git a/cinder/tests/unit/volume/drivers/gce/gce_mock.py b/cinder/tests/unit/volume/drivers/gce/gce_mock.py index c449198..4a8adc9 100644 --- a/cinder/tests/unit/volume/drivers/gce/gce_mock.py +++ b/cinder/tests/unit/volume/drivers/gce/gce_mock.py @@ -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' diff --git a/cinder/tests/unit/volume/drivers/gce/test_gce.py b/cinder/tests/unit/volume/drivers/gce/test_gce.py index 20259fb..9e75615 100644 --- a/cinder/tests/unit/volume/drivers/gce/test_gce.py +++ b/cinder/tests/unit/volume/drivers/gce/test_gce.py @@ -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' diff --git a/cinder/tests/unit/volume/drivers/test_ec2.py b/cinder/tests/unit/volume/drivers/test_ec2.py index 09229b0..9f95a9c 100644 --- a/cinder/tests/unit/volume/drivers/test_ec2.py +++ b/cinder/tests/unit/volume/drivers/test_ec2.py @@ -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): diff --git a/cinder/volume/drivers/aws/ebs.py b/cinder/volume/drivers/aws/ebs.py index 5881c4a..8df488c 100644 --- a/cinder/volume/drivers/aws/ebs.py +++ b/cinder/volume/drivers/aws/ebs.py @@ -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,21 +73,21 @@ 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, - aws_secret_access_key=CONF.AWS.secret_key, - region=region) - # resort to first AZ for now. TODO: expose this through API + 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(do_setup): expose this through API az = CONF.AWS.az try: self._zone = filter(lambda z: z.name == az, - self._conn.get_all_zones())[0] + self._conn.get_all_zones())[0] except IndexError: raise AvailabilityZoneNotFound(az=az) 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,11 +122,12 @@ 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'], - 'uuid': volume['id'], - 'is_clone': False, - 'created_at': volume['created_at'], - 'Name': volume['display_name']}) + self._conn.create_tags([ebs_vol.id], + {'project_id': volume['project_id'], + 'uuid': volume['id'], + 'is_clone': False, + 'created_at': volume['created_at'], + 'Name': volume['display_name']}) def _find(self, obj_id, find_func): ebs_objs = find_func(filters={'tag:uuid': obj_id}) @@ -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,11 +204,12 @@ 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'], - 'uuid': snapshot['id'], - 'is_clone': True, - 'created_at': snapshot['created_at'], - 'Name': snapshot['display_name']}) + self._conn.create_tags([ebs_snap.id], + {'project_id': snapshot['project_id'], + 'uuid': snapshot['id'], + 'is_clone': True, + 'created_at': snapshot['created_at'], + 'Name': snapshot['display_name']}) def delete_snapshot(self, snapshot): try: @@ -222,11 +229,12 @@ 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'], - 'uuid': volume['id'], - 'is_clone': False, - 'created_at': volume['created_at'], - 'Name': volume['display_name']}) + self._conn.create_tags([ebs_vol.id], + {'project_id': volume['project_id'], + 'uuid': volume['id'], + 'is_clone': False, + 'created_at': volume['created_at'], + 'Name': volume['display_name']}) def copy_image_to_volume(self, context, volume, image_service, image_id): raise NotImplemented() @@ -239,7 +247,3 @@ class EBSDriver(BaseVD): def copy_volume_data(self, context, src_vol, dest_vol, remote=None): raise NotImplemented() - - - - diff --git a/cinder/volume/drivers/aws/exception.py b/cinder/volume/drivers/aws/exception.py index d9bbce9..423379c 100644 --- a/cinder/volume/drivers/aws/exception.py +++ b/cinder/volume/drivers/aws/exception.py @@ -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") diff --git a/cinder/volume/drivers/gce/gceutils.py b/cinder/volume/drivers/gce/gceutils.py index a0480de..6b821dd 100644 --- a/cinder/volume/drivers/gce/gceutils.py +++ b/cinder/volume/drivers/gce/gceutils.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 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 ` 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 diff --git a/glance/aws/create-glance-images-aws.py b/glance/aws/create-glance-images-aws.py index 966fec9..659e59d 100644 --- a/glance/aws/create-glance-images-aws.py +++ b/glance/aws/create-glance-images-aws.py @@ -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 -''' +""" +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,26 +19,28 @@ 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'} + 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'} - def register_aws_images(self): - response = self.ec2_client.describe_images(Owners=['self']) - images = response['Images'] + def register_aws_images(self): + response = self.ec2_client.describe_images(Owners=['self']) + images = response['Images'] - for img in images: - self.create_image(self._aws_to_ostack_formatter(img)) + for img in images: + self.create_image(self._aws_to_ostack_formatter(img)) + + def create_image(self, img_data): + """Create an OpenStack image. - def create_image(self, img_data): - """ - Create an OpenStack image. :param img_data: dict -- Describes AWS AMI :returns: dict -- Response from REST call :raises: requests.HTTPError @@ -58,20 +53,21 @@ 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' - self.update_properties(glance_id, img_props) + # 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 pass except requests.HTTPError as e: raise e - def update_properties(self, imageid, props): - """ - Add or update a set of image properties on an image. + def update_properties(self, imageid, props): + """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,17 +80,18 @@ 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): + def _get_image_uuid(self, ami_id): md = hashlib.md5() md.update(ami_id) return str(uuid.UUID(bytes=md.digest())) - def _aws_to_ostack_formatter(self, aws_obj): - """ - Converts aws img data to Openstack img data format. + def _aws_to_ostack_formatter(self, aws_obj): + """Converts aws img data to Openstack img data format. + :param img(dict): aws img data :return(dict): ostack img data """ @@ -105,30 +102,32 @@ 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, - 'name' : aws_obj.get('Name') or aws_obj.get('ImageId'), - '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', - 'aws_image_id' : aws_obj.get('ImageId'), + 'id': image_id, + 'name': aws_obj.get('Name') or aws_obj.get('ImageId'), + 'container_format': self.aws_image_types[aws_obj.get('ImageType')], + 'disk_format': self.aws_image_types[aws_obj.get('ImageType')], + 'visibility': visibility, + '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), - 'aws_num_istore_vols' : str(num_istore_vols), + 'aws_ebs_vol_sizes': str(ebs_vol_sizes), + 'aws_num_istore_vols': str(num_istore_vols), } - 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') - self.sess = session.Session(auth=v3_auth, verify=False) # verify=True + 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') + 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() - diff --git a/glance/gce/create-glance-images-gce.py b/glance/gce/create-glance-images-gce.py index 9e35a67..c309857 100644 --- a/glance/gce/create-glance-images-gce.py +++ b/glance/gce/create-glance-images-gce.py @@ -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 -''' +""" +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 diff --git a/glance/gce/gceutils.py b/glance/gce/gceutils.py index dfd406e..4e49e73 100644 --- a/glance/gce/gceutils.py +++ b/glance/gce/gceutils.py @@ -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__) diff --git a/glance/glance_store/_drivers/aws.py b/glance/glance_store/_drivers/aws.py index c01e9b4..6f8d41c 100644 --- a/glance/glance_store/_drivers/aws.py +++ b/glance/glance_store/_drivers/aws.py @@ -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'), - cfg.StrOpt('secret_key', help='AWS secret access key'), - cfg.StrOpt('region_name', help='AWS region name'), -] +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')] + class StoreLocation(glance_store.location.StoreLocation): @@ -85,7 +79,7 @@ class Store(glance_store.driver.Store): def __init__(self, conf): super(Store, self).__init__(conf) conf.register_group(aws_opts_group) - conf.register_opts(aws_opts, group = aws_opts_group) + conf.register_opts(aws_opts, group=aws_opts_group) self.credentials = {} self.credentials['aws_access_key_id'] = conf.aws.access_key self.credentials['aws_secret_access_key'] = conf.aws.secret_key @@ -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 diff --git a/glance/glance_store/_drivers/gce.py b/glance/glance_store/_drivers/gce.py index 82bfd9b..d1313a4 100644 --- a/glance/glance_store/_drivers/gce.py +++ b/glance/glance_store/_drivers/gce.py @@ -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 diff --git a/glance/glance_store/tests/unit/gce/gce_mock.py b/glance/glance_store/tests/unit/gce/gce_mock.py index e58bdc7..32edd7e 100644 --- a/glance/glance_store/tests/unit/gce/gce_mock.py +++ b/glance/glance_store/tests/unit/gce/gce_mock.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 os diff --git a/glance/glance_store/tests/unit/gce/test_gce.py b/glance/glance_store/tests/unit/gce/test_gce.py index a822993..8f3e8c6 100644 --- a/glance/glance_store/tests/unit/gce/test_gce.py +++ b/glance/glance_store/tests/unit/gce/test_gce.py @@ -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 diff --git a/neutron/neutron/common/aws_utils.py b/neutron/neutron/common/aws_utils.py index fd4e07d..18874d3 100644 --- a/neutron/neutron/common/aws_utils.py +++ b/neutron/neutron/common/aws_utils.py @@ -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,28 +38,31 @@ 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): error_code = e.response['Error']['Code'] error_message = e.response['Error']['Message'] raise exceptions.AwsException(error_code=error_code, - message=error_message) + message=error_message) elif isinstance(e, exceptions.AwsException): # If the exception is already an AwsException, do not nest it. # 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) + message=error_message) + def aws_exception(fn): def wrapper(*args, **kwargs): @@ -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,15 +180,17 @@ 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'] if allocation_id is None: raise exceptions.AwsException(error_code="Allocation ID", - message="Allocation ID not found") + message="Allocation ID not found") return self._get_ec2_client().associate_address( DryRun=dry_run, InstanceId=ec2_instance_id, @@ -193,15 +206,17 @@ 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'] if association_id is None: raise exceptions.AwsException(error_code="Association ID", - message="Association ID not found") + message="Association ID not found") return self._get_ec2_client().disassociate_address( DryRun=dry_run, AssociationId=association_id @@ -209,20 +224,22 @@ 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'] if allocation_id is None: raise exceptions.AwsException(error_code="Allocation ID", - message="Allocation ID not found") + message="Allocation ID not found") return self._get_ec2_client().release_address( DryRun=dry_run, AllocationId=allocation_id) # 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 @@ -275,7 +290,7 @@ class AwsUtils: waiter = self._get_ec2_client().get_waiter('subnet_available') waiter.wait( DryRun=dry_run, - SubnetIds = [ subnet.id ]) + SubnetIds=[subnet.id]) subnet.create_tags(Tags=tags_list) @aws_exception @@ -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) @@ -461,33 +483,28 @@ class AwsUtils: def create_security_group_rules(self, ec2_secgrp, rules): if self._create_sec_grp_rules(ec2_secgrp, rules) is False: exceptions.AwsException( - message='Timed out creating security groups', - error_code='Time Out') + message='Timed out creating security groups', + error_code='Time Out') def create_security_group(self, name, description, vpc_id, os_secgrp_id, tags): if not description: description = 'Created by Platform9 OpenStack' secgrp = self._get_ec2_resource().create_security_group( - GroupName=name, Description=description, VpcId=vpc_id) + 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') + message='Timed out creating tags on security group', + error_code='Time Out') return secgrp @aws_exception - def get_sec_group_by_id(self, secgrp_id, vpc_id = None, dry_run=False): - filters = [{ - 'Name': 'tag-value', - 'Values': [secgrp_id] - }] + def get_sec_group_by_id(self, secgrp_id, vpc_id=None, dry_run=False): + 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) - diff --git a/neutron/neutron/common/gceutils.py b/neutron/neutron/common/gceutils.py index 6151a39..a97cd4d 100644 --- a/neutron/neutron/common/gceutils.py +++ b/neutron/neutron/common/gceutils.py @@ -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 @@ -66,7 +64,7 @@ class _FixedIntervalWithTimeoutLoopingCall(loopingcall.LoopingCallBase): # definition _FixedIntervalWithTimeoutLoopingCall if not hasattr(loopingcall, 'FixedIntervalWithTimeoutLoopingCall'): loopingcall.FixedIntervalWithTimeoutLoopingCall = \ - _FixedIntervalWithTimeoutLoopingCall + _FixedIntervalWithTimeoutLoopingCall class GceOperationError(Exception): @@ -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 ` 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 diff --git a/neutron/neutron/plugins/ml2/drivers/aws/callbacks.py b/neutron/neutron/plugins/ml2/drivers/aws/callbacks.py index 6a13e44..541255d 100644 --- a/neutron/neutron/plugins/ml2/drivers/aws/callbacks.py +++ b/neutron/neutron/plugins/ml2/drivers/aws/callbacks.py @@ -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) diff --git a/neutron/neutron/plugins/ml2/drivers/aws/mechanism_aws.py b/neutron/neutron/plugins/ml2/drivers/aws/mechanism_aws.py index 4725d2a..770784d 100644 --- a/neutron/neutron/plugins/ml2/drivers/aws/mechanism_aws.py +++ b/neutron/neutron/plugins/ml2/drivers/aws/mechanism_aws.py @@ -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": - # 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']) + 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 {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[ - 'provider:physical_network'] == "external": - LOG.error("Deleting provider and external networks not supported") - return + 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") + 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,18 +212,15 @@ 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): core_plugin = manager.NeutronManager.get_plugin() vpc_id = self.aws_utils.get_vpc_from_neutron_network_id( - context.current['network_id']) + context.current['network_id']) for secgrp_id in secgrp_ids: tags = [ {'Key': 'openstack_id', 'Value': secgrp_id}, @@ -222,7 +236,7 @@ class AwsMechanismDriver(api.MechanismDriver): desc = secgrp['description'] rules = secgrp['security_group_rules'] ec2_secgrp = self.aws_utils.create_security_group( - grp_name, desc, vpc_id, secgrp_id, tags) + grp_name, desc, vpc_id, secgrp_id, tags) self.aws_utils.create_security_group_rules(ec2_secgrp, rules) def delete_security_group(self, security_group_id): @@ -234,7 +248,7 @@ class AwsMechanismDriver(api.MechanismDriver): secgrp_id = rule['security_group_id'] secgrp = core_plugin.get_security_group(context, secgrp_id) old_rules = secgrp['security_group_rules'] - for idx in range(len(old_rules)-1, -1, -1): + for idx in range(len(old_rules) - 1, -1, -1): if old_rules[idx]['id'] == rule_id: old_rules.pop(idx) self.aws_utils.update_sec_group(secgrp_id, old_rules) @@ -253,7 +267,7 @@ class AwsMechanismDriver(api.MechanismDriver): secgrp_id = rule['security_group_id'] secgrp = core_plugin.get_security_group(context, secgrp_id) old_rules = secgrp['security_group_rules'] - for idx in range(len(old_rules)-1, -1, -1): + for idx in range(len(old_rules) - 1, -1, -1): if old_rules[idx]['id'] == rule_id: old_rules.pop(idx) break @@ -262,7 +276,7 @@ class AwsMechanismDriver(api.MechanismDriver): def secgroup_callback(self, resource, event, trigger, **kwargs): if resource == resources.SECURITY_GROUP: - if event == events.BEFORE_DELETE: + if event == events.BEFORE_DELETE: security_group_id = kwargs.get('security_group_id') if security_group_id: self.delete_security_group(security_group_id) @@ -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) - - diff --git a/neutron/neutron/plugins/ml2/drivers/gce/mech_gce.py b/neutron/neutron/plugins/ml2/drivers/gce/mech_gce.py index baa4877..65f9891 100644 --- a/neutron/neutron/plugins/ml2/drivers/gce/mech_gce.py +++ b/neutron/neutron/plugins/ml2/drivers/gce/mech_gce.py @@ -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) diff --git a/neutron/neutron/services/l3_router/aws_router_plugin.py b/neutron/neutron/services/l3_router/aws_router_plugin.py index df7d173..b830eb4 100644 --- a/neutron/neutron/services/l3_router/aws_router_plugin.py +++ b/neutron/neutron/services/l3_router/aws_router_plugin.py @@ -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,24 +21,20 @@ 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, - l3_hascheduler_db.L3_HA_scheduler_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. This class implements a L3 service plugin that provides @@ -47,13 +44,14 @@ 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, - security_group=securitygroups_db.SecurityGroup) + @resource_registry.tracked_resources( + router=l3_db.Router, floatingip=l3_db.FloatingIP, + security_group=securitygroups_db.SecurityGroup) def __init__(self): self.aws_utils = AwsUtils() super(AwsRouterPlugin, self).__init__() @@ -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) + (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,19 +176,23 @@ 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'])) + (router_name, ret_obj['id'])) return ret_obj except Exception as e: LOG.error("Error while creating router %s" % e) @@ -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) diff --git a/neutron/neutron/services/l3_router/gce_router_plugin.py b/neutron/neutron/services/l3_router/gce_router_plugin.py index 49edd1b..d65ed10 100644 --- a/neutron/neutron/services/l3_router/gce_router_plugin.py +++ b/neutron/neutron/services/l3_router/gce_router_plugin.py @@ -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) diff --git a/neutron/tests/common/gce/gce_mock.py b/neutron/tests/common/gce/gce_mock.py index 92e8284..0d98bcb 100644 --- a/neutron/tests/common/gce/gce_mock.py +++ b/neutron/tests/common/gce/gce_mock.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 os diff --git a/neutron/tests/plugins/ml2/drivers/gce/test_gce.py b/neutron/tests/plugins/ml2/drivers/gce/test_gce.py index a702736..96e82a7 100644 --- a/neutron/tests/plugins/ml2/drivers/gce/test_gce.py +++ b/neutron/tests/plugins/ml2/drivers/gce/test_gce.py @@ -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' diff --git a/nova/tests/unit/virt/ec2/test_ec2.py b/nova/tests/unit/virt/ec2/test_ec2.py index ea712f0..5f4be8b 100644 --- a/nova/tests/unit/virt/ec2/test_ec2.py +++ b/nova/tests/unit/virt/ec2/test_ec2.py @@ -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: diff --git a/nova/tests/unit/virt/gce/gce_mock.py b/nova/tests/unit/virt/gce/gce_mock.py index b2d2825..016a971 100644 --- a/nova/tests/unit/virt/gce/gce_mock.py +++ b/nova/tests/unit/virt/gce/gce_mock.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 os import six diff --git a/nova/tests/unit/virt/gce/test_gce.py b/nova/tests/unit/virt/gce/test_gce.py index 96fe623..3fb1db6 100644 --- a/nova/tests/unit/virt/gce/test_gce.py +++ b/nova/tests/unit/virt/gce/test_gce.py @@ -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' diff --git a/nova/virt/ec2/__init__.py b/nova/virt/ec2/__init__.py index 267e2f8..7dfb2ad 100644 --- a/nova/virt/ec2/__init__.py +++ b/nova/virt/ec2/__init__.py @@ -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 diff --git a/nova/virt/ec2/cloud_burst_filter.py b/nova/virt/ec2/cloud_burst_filter.py index ede5f64..483a5e0 100644 --- a/nova/virt/ec2/cloud_burst_filter.py +++ b/nova/virt/ec2/cloud_burst_filter.py @@ -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'] diff --git a/nova/virt/ec2/ec2driver.py b/nova/virt/ec2/ec2driver.py index 2f8b870..e13391a 100644 --- a/nova/virt/ec2/ec2driver.py +++ b/nova/virt/ec2/ec2driver.py @@ -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), @@ -70,7 +67,7 @@ aws_opts = [ ] CONF = cfg.CONF -#CONF.import_opt('my_ip', 'nova.netconf') +# CONF.import_opt('my_ip', 'nova.netconf') CONF.register_group(aws_group) CONF.register_opts(aws_opts, group=aws_group) @@ -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,28 +181,27 @@ 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, - aws_secret_access_key=CONF.AWS.secret_key, - region=region) + self.ec2_conn = ec2.EC2Connection( + aws_access_key_id=CONF.AWS.access_key, + aws_secret_access_key=CONF.AWS.secret_key, + region=region) self.cloudwatch_conn = ec2.cloudwatch.connect_to_region( aws_region, aws_access_key_id=CONF.AWS.access_key, 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 @@ -335,18 +335,20 @@ class EC2Driver(driver.ComputeDriver): image_ami_id = self._get_image_ami_id_from_meta(context, image_meta) subnet_id, fixed_ip, port_id, network_id = self._process_network_info( - 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,17 +358,17 @@ 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'], - image_id=image_ami_id, user_data=user_data, - subnet_id=subnet_id, private_ip_address=fixed_ip, - security_group_ids=security_groups) + instance_type=flavor_type, key_name=instance['key_name'], + image_id=image_ami_id, user_data=user_data, + subnet_id=subnet_id, private_ip_address=fixed_ip, + security_group_ids=security_groups) 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) @@ -455,7 +458,7 @@ class EC2Driver(driver.ComputeDriver): :param reboot_type: Either a HARD or SOFT reboot :param block_device_info: Info pertaining to attached volumes :param bad_volumes_callback: Function to handle any bad volumes - encountered + encountered """ if reboot_type == 'SOFT': self._soft_reboot( @@ -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) @@ -603,7 +607,7 @@ class EC2Driver(driver.ComputeDriver): try: ec2_id = self._get_ec2_id_from_instance(instance) ec2_instances = self.ec2_conn.get_only_instances( - instance_ids=[ec2_id]) + instance_ids=[ec2_id]) except exception.InstanceNotFound as ex: # Exception while fetching instance info from AWS LOG.exception('Exception in destroy while fetching EC2 id for ' @@ -625,13 +629,12 @@ class EC2Driver(driver.ComputeDriver): power_state.SHUTDOWN) except Exception as ex: LOG.exception("Exception while destroying instance: %s" % - str(ex)) + str(ex)) raise ex 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 @@ -710,11 +713,11 @@ class EC2Driver(driver.ComputeDriver): elif 'metadata' in instance and 'ec2_id' in instance['metadata']: ec2_id = instance['metadata']['ec2_id'] ec2_instances = 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) if len(ec2_instances) == 0: LOG.warning(_("EC2 instance with ID %s not found") % ec2_id, - instance=instance) + instance=instance) raise exception.InstanceNotFound(instance_id=instance['name']) ec2_instance = ec2_instances[0] else: @@ -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,13 +742,12 @@ 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, - max_results=None) + 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, - instance=instance) + instance=instance) raise exception.InstanceNotFound(instance_id=instance['name']) ec2_instance = ec2_instances[0] @@ -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") @@ -813,10 +812,9 @@ class EC2Driver(driver.ComputeDriver): 'port': 5901} def get_spice_console(self, instance): - """ Simple Protocol for Independent Computing Environments + """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,28 +1040,33 @@ 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 + # waiting for the power state to change timer = loopingcall.FixedIntervalLoopingCall(_wait_for_power_state) timer.start(interval=1).wait() 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, - executable_by=None, filters=None, dry_run=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: LOG.info("Image has changed state to %s." % desired_state) diff --git a/nova/virt/ec2/exception_handler.py b/nova/virt/ec2/exception_handler.py index 0ca85c4..6402763 100644 --- a/nova/virt/ec2/exception_handler.py +++ b/nova/virt/ec2/exception_handler.py @@ -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) \ No newline at end of file + return exception.NovaException(ec2_response_error_exc.message) diff --git a/nova/virt/gce/__init__.py b/nova/virt/gce/__init__.py index 4bf42af..9e51d05 100644 --- a/nova/virt/gce/__init__.py +++ b/nova/virt/gce/__init__.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. +""" from nova.virt.gce import driver diff --git a/nova/virt/gce/constants.py b/nova/virt/gce/constants.py index 18bb168..3dc9ece 100644 --- a/nova/virt/gce/constants.py +++ b/nova/virt/gce/constants.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. +""" from nova.compute import power_state diff --git a/nova/virt/gce/create-nova-flavors-gce.py b/nova/virt/gce/create-nova-flavors-gce.py index fa20574..ea19b53 100644 --- a/nova/virt/gce/create-nova-flavors-gce.py +++ b/nova/virt/gce/create-nova-flavors-gce.py @@ -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 -''' +""" +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} '.format(sys.argv[0])) + print('Usage: {0} '.format( + sys.argv[0])) sys.exit(1) gce_flavors = GceFlavors(sys.argv[1], sys.argv[2], sys.argv[3]) gce_flavors.register_gce_flavors() diff --git a/nova/virt/gce/driver.py b/nova/virt/gce/driver.py index 75ab784..177b5a0 100644 --- a/nova/virt/gce/driver.py +++ b/nova/virt/gce/driver.py @@ -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 :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 - a driver that manages only one node can safely ignore this + """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): diff --git a/nova/virt/gce/gceutils.py b/nova/virt/gce/gceutils.py index 117344d..ad75813 100644 --- a/nova/virt/gce/gceutils.py +++ b/nova/virt/gce/gceutils.py @@ -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 @@ -63,7 +62,7 @@ class _FixedIntervalWithTimeoutLoopingCall(loopingcall.LoopingCallBase): # definition _FixedIntervalWithTimeoutLoopingCall if not hasattr(loopingcall, 'FixedIntervalWithTimeoutLoopingCall'): loopingcall.FixedIntervalWithTimeoutLoopingCall = \ - _FixedIntervalWithTimeoutLoopingCall + _FixedIntervalWithTimeoutLoopingCall class GceOperationError(Exception): @@ -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 @@ -125,7 +128,8 @@ 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 + """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 diff --git a/run_tests.sh b/run_tests.sh index 776a5c3..5a76876 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -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]}" diff --git a/setup.py b/setup.py index deef104..f692bd3 100644 --- a/setup.py +++ b/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 diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 8241733..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -hacking>=0.12.0,<0.13 # Apache-2.0 -zuul \ No newline at end of file diff --git a/tox.ini b/tox.ini index 441a33d..d4c4498 100644 --- a/tox.ini +++ b/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