Implement neutron network protection plugin
Closes-Bug: 1572006 Change-Id: I304db6de21d16e8dbff695ffa2c76c8f8cc373ee
This commit is contained in:
parent
5f5680fcb2
commit
d92876f7fa
|
@ -29,6 +29,21 @@ OPERATION_TYPES = (
|
|||
# plugin type
|
||||
PLUGIN_BANK = 'bank'
|
||||
|
||||
# supported network resource types
|
||||
NETWORK_RESOURCE_TYPES = (NET_RESOURCE_TYPE,
|
||||
SUBNET_RESOURCE_TYPE,
|
||||
ROUTER_RESOURCE_TYPE,
|
||||
ROUTERINTERFACE_RESOURCE_TYPE,
|
||||
PORT_RESOURCE_TYPE,
|
||||
SECURITYGROUP_RESOURCE_TYPE,
|
||||
) = ('OS::Neutron::Net',
|
||||
'OS::Neutron::Subnet',
|
||||
'OS::Neutron::Router',
|
||||
'OS::Neutron::RouterInterface',
|
||||
'OS::Neutron::Port',
|
||||
'OS::Neutron::SecurityGroup',
|
||||
)
|
||||
|
||||
# supported resource types
|
||||
RESOURCE_TYPES = (PROJECT_RESOURCE_TYPE,
|
||||
SERVER_RESOURCE_TYPE,
|
||||
|
|
|
@ -383,3 +383,8 @@ class CheckpointNotAvailable(KarborException):
|
|||
|
||||
class CheckpointNotBeDeleted(KarborException):
|
||||
message = _("The checkpoint %(checkpoint_id)s can not be deleted.")
|
||||
|
||||
|
||||
class GetProtectionNetworkSubResourceFailed(KarborException):
|
||||
message = _("Get protection network sub-resources of type %(type)s failed:"
|
||||
" %(reason)s")
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# 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.
|
||||
|
||||
OPTIONS_SCHEMA = {
|
||||
"title": "Network Protection Options",
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
|
||||
RESTORE_SCHEMA = {
|
||||
"title": "Network Protection Restore",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"restore_name": {
|
||||
"type": "string",
|
||||
"title": "Restore Network Name",
|
||||
"description": "The name of the restore network",
|
||||
},
|
||||
},
|
||||
"required": ["restore_name"]
|
||||
}
|
||||
|
||||
SAVED_INFO_SCHEMA = {
|
||||
"title": "Network Protection Saved Info",
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
|
@ -9,35 +9,556 @@
|
|||
# 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 six
|
||||
|
||||
from karbor.common import constants
|
||||
from karbor import exception
|
||||
from karbor.services.protection.client_factory import ClientFactory
|
||||
from karbor.services.protection import protection_plugin
|
||||
from karbor.services.protection.protection_plugins.network \
|
||||
import network_plugin_schemas
|
||||
from karbor.services.protection.restore_heat import HeatResource
|
||||
from oslo_log import log as logging
|
||||
from uuid import uuid4
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_network_id(cntxt):
|
||||
network_id = cntxt.project_id
|
||||
return network_id
|
||||
|
||||
|
||||
class ProtectOperation(protection_plugin.Operation):
|
||||
_SUPPORT_RESOURCE_TYPES = [constants.NETWORK_RESOURCE_TYPE]
|
||||
|
||||
def _get_resources_by_network(self, cntxt, neutron_client):
|
||||
try:
|
||||
networks = neutron_client.list_networks().get('networks')
|
||||
networks_metadata = {}
|
||||
|
||||
allowed_keys = [
|
||||
'id',
|
||||
'admin_state_up',
|
||||
'availability_zone_hints',
|
||||
'description',
|
||||
'ipv4_address_scope',
|
||||
'ipv6_address_scope',
|
||||
'mtu',
|
||||
'name',
|
||||
'port_security_enabled',
|
||||
'router:external',
|
||||
'shared',
|
||||
'status',
|
||||
'subnets',
|
||||
'tags',
|
||||
'tenant_id'
|
||||
]
|
||||
|
||||
for network in networks:
|
||||
network_metadata = {
|
||||
k: network[k] for k in network if k in allowed_keys}
|
||||
networks_metadata[network["id"]] = network_metadata
|
||||
return networks_metadata
|
||||
except Exception as e:
|
||||
LOG.exception("List all summary networks from neutron failed.")
|
||||
raise exception.GetProtectionNetworkSubResourceFailed(
|
||||
type=self._SUPPORT_RESOURCE_TYPES,
|
||||
reason=six.text_type(e))
|
||||
else:
|
||||
return []
|
||||
|
||||
def _get_resources_by_subnet(self, cntxt, neutron_client):
|
||||
try:
|
||||
subnets = neutron_client.list_subnets().get('subnets')
|
||||
subnets_metadata = {}
|
||||
|
||||
allowed_keys = [
|
||||
'cidr',
|
||||
'allocation_pools',
|
||||
'description',
|
||||
'dns_nameservers',
|
||||
'enable_dhcp',
|
||||
'gateway_ip',
|
||||
'host_routes',
|
||||
'id',
|
||||
'ip_version',
|
||||
'ipv6_address_mode',
|
||||
'ipv6_ra_mode',
|
||||
'name',
|
||||
'network_id',
|
||||
'subnetpool_id',
|
||||
'tenant_id'
|
||||
]
|
||||
|
||||
for subnet in subnets:
|
||||
subnet_metadata = {
|
||||
k: subnet[k] for k in subnet if k in allowed_keys}
|
||||
subnets_metadata[subnet["id"]] = subnet_metadata
|
||||
|
||||
return subnets_metadata
|
||||
except Exception as e:
|
||||
LOG.exception("List all summary subnets from neutron failed.")
|
||||
raise exception.GetProtectionNetworkSubResourceFailed(
|
||||
type=self._SUPPORT_RESOURCE_TYPES,
|
||||
reason=six.text_type(e))
|
||||
else:
|
||||
return []
|
||||
|
||||
def _get_resources_by_port(self, cntxt, neutron_client):
|
||||
try:
|
||||
ports = neutron_client.list_ports().get('ports')
|
||||
ports_metadata = {}
|
||||
|
||||
allowed_keys = [
|
||||
'admin_state_up',
|
||||
'allowed_address_pairs',
|
||||
'description',
|
||||
'device_id',
|
||||
'device_owner',
|
||||
'extra_dhcp_opts',
|
||||
'fixed_ips',
|
||||
'id',
|
||||
'mac_address',
|
||||
'name',
|
||||
'network_id',
|
||||
'port_security_enabled',
|
||||
'security_groups',
|
||||
'status',
|
||||
'tenant_id'
|
||||
]
|
||||
|
||||
for port in ports:
|
||||
port_metadata = {
|
||||
k: port[k] for k in port if k in allowed_keys}
|
||||
ports_metadata[port["id"]] = port_metadata
|
||||
return ports_metadata
|
||||
except Exception as e:
|
||||
LOG.exception("List all summary ports from neutron failed.")
|
||||
raise exception.GetProtectionNetworkSubResourceFailed(
|
||||
type=self._SUPPORT_RESOURCE_TYPES,
|
||||
reason=six.text_type(e))
|
||||
else:
|
||||
return []
|
||||
|
||||
def _get_resources_by_router(self, cntxt, neutron_client):
|
||||
try:
|
||||
routers = neutron_client.list_routers().get('routers')
|
||||
routers_metadata = {}
|
||||
|
||||
allowed_keys = [
|
||||
'admin_state_u',
|
||||
'availability_zone_hints',
|
||||
'description',
|
||||
'external_gateway_info',
|
||||
'id',
|
||||
'name',
|
||||
'routes',
|
||||
'status'
|
||||
]
|
||||
|
||||
for router in routers:
|
||||
router_metadata = {
|
||||
k: router[k] for k in router if k in allowed_keys}
|
||||
routers_metadata[router["id"]] = router_metadata
|
||||
|
||||
return routers_metadata
|
||||
except Exception as e:
|
||||
LOG.exception("List all summary routers from neutron failed.")
|
||||
raise exception.GetProtectionNetworkSubResourceFailed(
|
||||
type=self._SUPPORT_RESOURCE_TYPES,
|
||||
reason=six.text_type(e))
|
||||
else:
|
||||
return []
|
||||
|
||||
def _get_resources_by_security_group(self, cntxt, neutron_client):
|
||||
try:
|
||||
sgs = neutron_client.list_security_groups().get('security_groups')
|
||||
sgs_metadata = {}
|
||||
|
||||
allowed_keys = [
|
||||
'id',
|
||||
'description',
|
||||
'name',
|
||||
'security_group_rules',
|
||||
'tenant_id'
|
||||
]
|
||||
|
||||
for sg in sgs:
|
||||
sg_metadata = {k: sg[k] for k in sg if k in allowed_keys}
|
||||
sgs_metadata[sg["id"]] = sg_metadata
|
||||
return sgs_metadata
|
||||
except Exception as e:
|
||||
LOG.exception("List all summary security_groups from neutron "
|
||||
"failed.")
|
||||
raise exception.GetProtectionNetworkSubResourceFailed(
|
||||
type=self._SUPPORT_RESOURCE_TYPES,
|
||||
reason=six.text_type(e))
|
||||
else:
|
||||
return []
|
||||
|
||||
def on_main(self, checkpoint, resource, context, parameters, **kwargs):
|
||||
network_id = get_network_id(context)
|
||||
backup_name = kwargs.get("backup_name", "karbor network backup")
|
||||
bank_section = checkpoint.get_resource_bank_section(network_id)
|
||||
neutron_client = ClientFactory.create_client("neutron", context)
|
||||
|
||||
resource_definition = {"resource_id": network_id}
|
||||
resource_definition["backup_name"] = backup_name
|
||||
resource_definition["network_metadata"] = (
|
||||
self._get_resources_by_network(context, neutron_client))
|
||||
resource_definition["subnet_metadata"] = (
|
||||
self._get_resources_by_subnet(context, neutron_client))
|
||||
resource_definition["port_metadata"] = (
|
||||
self._get_resources_by_port(context, neutron_client))
|
||||
resource_definition["router_metadata"] = (
|
||||
self._get_resources_by_router(context, neutron_client))
|
||||
resource_definition["security-group_metadata"] = (
|
||||
self._get_resources_by_security_group(context, neutron_client))
|
||||
|
||||
try:
|
||||
bank_section.update_object("status",
|
||||
constants.RESOURCE_STATUS_PROTECTING)
|
||||
|
||||
# write resource_definition in bank
|
||||
bank_section.update_object("metadata", resource_definition)
|
||||
|
||||
# update resource_definition backup_status
|
||||
bank_section.update_object("status",
|
||||
constants.CHECKPOINT_STATUS_AVAILABLE)
|
||||
LOG.info("finish backup network, network_id: %s.", network_id)
|
||||
except Exception as err:
|
||||
# update resource_definition backup_status
|
||||
LOG.error("create backup failed, network_id: %s.", network_id)
|
||||
bank_section.update_object("status",
|
||||
constants.CHECKPOINT_STATUS_ERROR)
|
||||
raise exception.CreateBackupFailed(
|
||||
reason=err,
|
||||
resource_id=network_id,
|
||||
resource_type=self._SUPPORT_RESOURCE_TYPES)
|
||||
|
||||
|
||||
class RestoreOperation(protection_plugin.Operation):
|
||||
def _heat_restore_networks(self, heat_template, nets_meta):
|
||||
for net_meta in nets_meta:
|
||||
net_data = nets_meta[net_meta]
|
||||
if net_data["router:external"]:
|
||||
continue
|
||||
heat_resource_id = net_data["name"]
|
||||
net_heat_resource = HeatResource(heat_resource_id,
|
||||
constants.NET_RESOURCE_TYPE)
|
||||
properties = {}
|
||||
properties["admin_state_up"] = net_data["admin_state_up"]
|
||||
properties["port_security_enabled"] = (
|
||||
net_data["port_security_enabled"])
|
||||
properties["shared"] = net_data["shared"]
|
||||
properties["name"] = net_data["name"]
|
||||
|
||||
for key, value in properties.items():
|
||||
net_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, net_heat_resource)
|
||||
|
||||
def _get_dependent_net(self, network_id, nets_meta):
|
||||
for net_meta in nets_meta:
|
||||
net_data = nets_meta[net_meta]
|
||||
if network_id == net_data["id"]:
|
||||
return net_data["name"]
|
||||
|
||||
def _is_external_subnet(self, network_id, nets_meta):
|
||||
for net_meta in nets_meta:
|
||||
net_data = nets_meta[net_meta]
|
||||
if network_id == net_data["id"]:
|
||||
return net_data["router:external"]
|
||||
|
||||
def _heat_restore_subnets(self, heat_template, nets_meta, subs_meta):
|
||||
for sub_meta in subs_meta:
|
||||
sub_data = subs_meta[sub_meta]
|
||||
|
||||
is_ext_subnet = self._is_external_subnet(sub_data["network_id"],
|
||||
nets_meta)
|
||||
|
||||
if is_ext_subnet:
|
||||
continue
|
||||
|
||||
heat_resource_id = sub_data["name"]
|
||||
sub_heat_resource = HeatResource(heat_resource_id,
|
||||
constants.SUBNET_RESOURCE_TYPE)
|
||||
properties = {}
|
||||
properties["cidr"] = sub_data["cidr"]
|
||||
properties["allocation_pools"] = sub_data["allocation_pools"]
|
||||
properties["dns_nameservers"] = sub_data["dns_nameservers"]
|
||||
properties["enable_dhcp"] = sub_data["enable_dhcp"]
|
||||
properties["gateway_ip"] = sub_data["gateway_ip"]
|
||||
properties["host_routes"] = sub_data["host_routes"]
|
||||
properties["name"] = sub_data["name"]
|
||||
properties["ip_version"] = sub_data["ip_version"]
|
||||
net_name = self._get_dependent_net(sub_data["network_id"],
|
||||
nets_meta)
|
||||
properties["network_id"] = (
|
||||
heat_template.get_resource_reference(net_name))
|
||||
properties["tenant_id"] = sub_data["tenant_id"]
|
||||
|
||||
for key, value in properties.items():
|
||||
sub_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, sub_heat_resource)
|
||||
|
||||
def _get_subnet_by_subnetid(self, subnet_id, subs_meta):
|
||||
for sub_meta in subs_meta:
|
||||
sub_data = subs_meta[sub_meta]
|
||||
if subnet_id == sub_data["id"]:
|
||||
return sub_data["name"]
|
||||
|
||||
return ""
|
||||
|
||||
def _get_new_fixed_ips(self, heat_template, subs_meta, fixed_ips_meta):
|
||||
new_fixed_ips = []
|
||||
for fixed_ip in fixed_ips_meta:
|
||||
properties = {}
|
||||
properties["ip_address"] = fixed_ip["ip_address"]
|
||||
subnet_name = self._get_subnet_by_subnetid(fixed_ip["subnet_id"],
|
||||
subs_meta)
|
||||
properties["subnet_id"] = (
|
||||
heat_template.get_resource_reference(subnet_name))
|
||||
new_fixed_ips.append(properties)
|
||||
|
||||
return new_fixed_ips
|
||||
|
||||
def _heat_restore_ports(self, heat_template,
|
||||
nets_meta, subs_meta, ports_meta):
|
||||
for port_meta in ports_meta:
|
||||
port_data = ports_meta[port_meta]
|
||||
heat_resource_id = port_data["name"]
|
||||
port_heat_resource = HeatResource(heat_resource_id,
|
||||
constants.PORT_RESOURCE_TYPE)
|
||||
|
||||
if (port_data["device_owner"] == "network:router_interface") or (
|
||||
port_data["device_owner"] == "network:router_gateway") or (
|
||||
port_data["device_owner"] == "network:dhcp") or (
|
||||
port_data["device_owner"] == "network:floatingip"):
|
||||
continue
|
||||
|
||||
properties = {}
|
||||
properties["admin_state_up"] = port_data["admin_state_up"]
|
||||
properties["allowed_address_pairs"] = (
|
||||
port_data["allowed_address_pairs"])
|
||||
properties["device_id"] = port_data["device_id"]
|
||||
properties["device_owner"] = port_data["device_owner"]
|
||||
new_fixed_ips = self._get_new_fixed_ips(heat_template,
|
||||
subs_meta,
|
||||
port_data["fixed_ips"])
|
||||
properties["fixed_ips"] = new_fixed_ips
|
||||
properties["mac_address"] = port_data["mac_address"]
|
||||
properties["name"] = port_data["name"]
|
||||
net_name = self._get_dependent_net(port_data["network_id"],
|
||||
nets_meta)
|
||||
properties["network_id"] = (
|
||||
heat_template.get_resource_reference(net_name))
|
||||
properties["port_security_enabled"] = (
|
||||
port_data["port_security_enabled"])
|
||||
properties["security_groups"] = port_data["security_groups"]
|
||||
|
||||
for key, value in properties.items():
|
||||
port_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, port_heat_resource)
|
||||
|
||||
def _get_new_external_gateway(self, public_network_id,
|
||||
gateway_info, subs_meta, neutron_client):
|
||||
new_ext_gw = {}
|
||||
|
||||
# get public network id
|
||||
if not public_network_id:
|
||||
networks = neutron_client.list_networks().get('networks')
|
||||
for network in networks:
|
||||
if network['router:external'] is True:
|
||||
public_network_id = network['id']
|
||||
break
|
||||
|
||||
new_ext_gw["network"] = public_network_id
|
||||
new_ext_gw["enable_snat"] = gateway_info["enable_snat"]
|
||||
return new_ext_gw
|
||||
|
||||
def _heat_restore_routers(self, public_network_id, heat_template,
|
||||
subs_meta, routers_meta, neutron_client):
|
||||
for router_meta in routers_meta:
|
||||
router_data = routers_meta[router_meta]
|
||||
heat_resource_id = router_data["name"]
|
||||
router_heat_resource = HeatResource(heat_resource_id,
|
||||
constants.ROUTER_RESOURCE_TYPE)
|
||||
properties = {}
|
||||
org_external_gateway = router_data["external_gateway_info"]
|
||||
new_external_gateway = (
|
||||
self._get_new_external_gateway(public_network_id,
|
||||
org_external_gateway,
|
||||
subs_meta,
|
||||
neutron_client))
|
||||
properties["external_gateway_info"] = new_external_gateway
|
||||
properties["name"] = router_data["name"]
|
||||
|
||||
for key, value in properties.items():
|
||||
router_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, router_heat_resource)
|
||||
|
||||
def _get_router_name(self, device_id, routers_meta):
|
||||
for router_meta in routers_meta:
|
||||
router_data = routers_meta[router_meta]
|
||||
if device_id == router_data["id"]:
|
||||
return router_data["name"]
|
||||
|
||||
return ""
|
||||
|
||||
def _get_subnet_name_by_fixed_ips(self, fixed_ips, subs_meta):
|
||||
subnet_id = fixed_ips[0]["subnet_id"]
|
||||
for sub_meta in subs_meta:
|
||||
sub_data = subs_meta[sub_meta]
|
||||
if subnet_id == sub_data["id"]:
|
||||
return sub_data["name"]
|
||||
|
||||
return ""
|
||||
|
||||
def _heat_restore_routerinterfaces(self, heat_template,
|
||||
subs_meta, routers_meta, ports_meta):
|
||||
for port_meta in ports_meta:
|
||||
port_data = ports_meta[port_meta]
|
||||
heat_resource_id = str(uuid4())
|
||||
port_heat_resource = (
|
||||
HeatResource(heat_resource_id,
|
||||
constants.ROUTERINTERFACE_RESOURCE_TYPE))
|
||||
|
||||
if port_data["device_owner"] != "network:router_interface":
|
||||
continue
|
||||
|
||||
properties = {}
|
||||
router_name = self._get_router_name(port_data["device_id"],
|
||||
routers_meta)
|
||||
properties["router"] = (
|
||||
heat_template.get_resource_reference(router_name))
|
||||
subnet_name = (
|
||||
self._get_subnet_name_by_fixed_ips(port_data["fixed_ips"],
|
||||
subs_meta))
|
||||
properties["subnet"] = (
|
||||
heat_template.get_resource_reference(subnet_name))
|
||||
|
||||
for key, value in properties.items():
|
||||
port_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, port_heat_resource)
|
||||
|
||||
def _get_security_group_rules(self, security_group_rules):
|
||||
new_security_group_rules = []
|
||||
for sg in security_group_rules:
|
||||
if sg["remote_ip_prefix"] is None:
|
||||
continue
|
||||
|
||||
security_group_rule = {}
|
||||
security_group_rule["direction"] = sg["direction"]
|
||||
security_group_rule["ethertype"] = sg["ethertype"]
|
||||
security_group_rule["port_range_max"] = sg["port_range_max"]
|
||||
security_group_rule["port_range_min"] = sg["port_range_min"]
|
||||
security_group_rule["protocol"] = sg["protocol"]
|
||||
security_group_rule["remote_group_id"] = sg["remote_group_id"]
|
||||
security_group_rule["remote_ip_prefix"] = sg["remote_ip_prefix"]
|
||||
|
||||
if "remote_mode" in sg:
|
||||
security_group_rule["remote_mode"] = sg["remote_mode"]
|
||||
new_security_group_rules.append(security_group_rule)
|
||||
|
||||
return new_security_group_rules
|
||||
|
||||
def _heat_restore_securitygroups(self, heat_template, sgs_meta):
|
||||
for sg_meta in sgs_meta:
|
||||
sg_data = sgs_meta[sg_meta]
|
||||
|
||||
# Skip the default securitygroups
|
||||
if sg_data["name"] == "default":
|
||||
continue
|
||||
|
||||
heat_resource_id = sg_data["name"]
|
||||
sg_heat_resource = (
|
||||
HeatResource(heat_resource_id,
|
||||
constants.SECURITYGROUP_RESOURCE_TYPE))
|
||||
properties = {}
|
||||
sg_rules = sg_data["security_group_rules"]
|
||||
properties["description"] = sg_data["description"]
|
||||
properties["name"] = sg_data["name"]
|
||||
properties["rules"] = self._get_security_group_rules(sg_rules)
|
||||
|
||||
for key, value in properties.items():
|
||||
sg_heat_resource.set_property(key, value)
|
||||
heat_template.put_resource(heat_resource_id, sg_heat_resource)
|
||||
|
||||
def on_main(self, checkpoint, resource, context,
|
||||
parameters, heat_template, **kwargs):
|
||||
neutron_client = ClientFactory.create_client("neutron", context)
|
||||
network_id = get_network_id(context)
|
||||
public_network_id = parameters.get("public_network_id")
|
||||
bank_section = checkpoint.get_resource_bank_section(network_id)
|
||||
|
||||
try:
|
||||
resource_definition = bank_section.get_object("metadata")
|
||||
|
||||
# Config Net
|
||||
if "network_metadata" in resource_definition:
|
||||
nets_meta = resource_definition["network_metadata"]
|
||||
self._heat_restore_networks(heat_template, nets_meta)
|
||||
|
||||
# Config Subnet
|
||||
if "subnet_metadata" in resource_definition:
|
||||
subs_meta = resource_definition["subnet_metadata"]
|
||||
self._heat_restore_subnets(heat_template, nets_meta, subs_meta)
|
||||
|
||||
# Config Port
|
||||
if "port_metadata" in resource_definition:
|
||||
ports_meta = resource_definition["port_metadata"]
|
||||
self._heat_restore_ports(heat_template, nets_meta,
|
||||
subs_meta, ports_meta)
|
||||
|
||||
# Config Router
|
||||
if "router_metadata" in resource_definition:
|
||||
routers_meta = resource_definition["router_metadata"]
|
||||
self._heat_restore_routers(public_network_id,
|
||||
heat_template,
|
||||
subs_meta,
|
||||
routers_meta,
|
||||
neutron_client)
|
||||
|
||||
# Config RouterInterface
|
||||
if [subs_meta is not None] and (
|
||||
[routers_meta is not None] and [ports_meta is not None]):
|
||||
self._heat_restore_routerinterfaces(heat_template, subs_meta,
|
||||
routers_meta, ports_meta)
|
||||
|
||||
# Config Securiy-group
|
||||
if "security-group_metadata" in resource_definition:
|
||||
sgs_meta = resource_definition["security-group_metadata"]
|
||||
self._heat_restore_securitygroups(heat_template, sgs_meta)
|
||||
|
||||
except Exception as e:
|
||||
LOG.error("restore network backup failed, network_id: %s.",
|
||||
network_id)
|
||||
raise exception.RestoreBackupFailed(
|
||||
reason=six.text_type(e),
|
||||
resource_id=network_id,
|
||||
resource_type=constants.NETWORK_RESOURCE_TYPE
|
||||
)
|
||||
|
||||
|
||||
class NeutronProtectionPlugin(protection_plugin.ProtectionPlugin):
|
||||
_SUPPORT_RESOURCE_TYPES = [constants.NETWORK_RESOURCE_TYPE]
|
||||
|
||||
def __init__(self, config=None):
|
||||
super(NeutronProtectionPlugin, self).__init__(config)
|
||||
|
||||
@classmethod
|
||||
def get_supported_resources_types(self):
|
||||
return self._SUPPORT_RESOURCE_TYPES
|
||||
|
||||
@classmethod
|
||||
def get_options_schema(self, resources_type):
|
||||
# TODO(chenhuayi)
|
||||
pass
|
||||
return network_plugin_schemas.OPTIONS_SCHEMA
|
||||
|
||||
@classmethod
|
||||
def get_restore_schema(self, resources_type):
|
||||
# TODO(chenhuayi)
|
||||
pass
|
||||
return network_plugin_schemas.RESTORE_SCHEMA
|
||||
|
||||
@classmethod
|
||||
def get_saved_info_schema(self, resources_type):
|
||||
# TODO(chenhuayi)
|
||||
pass
|
||||
return network_plugin_schemas.SAVED_INFO_SCHEMA
|
||||
|
||||
@classmethod
|
||||
def get_saved_info(self, metadata_store, resource):
|
||||
|
@ -45,12 +566,10 @@ class NeutronProtectionPlugin(protection_plugin.ProtectionPlugin):
|
|||
pass
|
||||
|
||||
def get_protect_operation(self, resource):
|
||||
# TODO(chenhuayi)
|
||||
pass
|
||||
return ProtectOperation()
|
||||
|
||||
def get_restore_operation(self, resource):
|
||||
# TODO(chenhuayi)
|
||||
pass
|
||||
return RestoreOperation()
|
||||
|
||||
def get_delete_operation(self, resource):
|
||||
# TODO(chenhuayi)
|
||||
|
|
|
@ -170,6 +170,25 @@ class RestoresTest(karbor_base.KarborBaseTest):
|
|||
self.assertEqual(1, len(item.resources_status))
|
||||
self._store(item.resources_status)
|
||||
|
||||
def test_restore_network_resources(self):
|
||||
network = self.store(objects.Network())
|
||||
network.create()
|
||||
plan = self.store(objects.Plan())
|
||||
plan.create(self.provider_id_os, [network, ])
|
||||
checkpoint = self.store(objects.Checkpoint())
|
||||
checkpoint.create(self.provider_id_os, plan.id)
|
||||
network.close()
|
||||
|
||||
restore_target = self.get_restore_target(self.keystone_endpoint)
|
||||
restore = self.store(objects.Restore())
|
||||
restore.create(self.provider_id_os, checkpoint.id,
|
||||
restore_target, self.parameters, self.restore_auth)
|
||||
|
||||
item = self.karbor_client.restores.get(restore.id)
|
||||
self.assertEqual(constants.RESTORE_STATUS_SUCCESS,
|
||||
item.status)
|
||||
self._store(item.resources_status)
|
||||
|
||||
def test_restore_resources_with_fs_bank(self):
|
||||
volume = self.store(objects.Volume())
|
||||
volume.create(1)
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
# 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 collections
|
||||
from karbor.common import constants
|
||||
from karbor.context import RequestContext
|
||||
from karbor.resource import Resource
|
||||
from karbor.services.protection.bank_plugin import Bank
|
||||
from karbor.services.protection.bank_plugin import BankPlugin
|
||||
from karbor.services.protection.bank_plugin import BankSection
|
||||
from karbor.services.protection import client_factory
|
||||
from karbor.services.protection.protection_plugins.network \
|
||||
import network_plugin_schemas
|
||||
from karbor.services.protection.protection_plugins.network. \
|
||||
neutron_protection_plugin import NeutronProtectionPlugin
|
||||
from karbor.tests import base
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
FakeNetworks = {'networks': [
|
||||
{u'status': u'ACTIVE',
|
||||
u'router:external': False,
|
||||
u'availability_zone_hints': [],
|
||||
u'availability_zones': [u'nova'],
|
||||
u'ipv4_address_scope': None,
|
||||
u'description': u'',
|
||||
u'provider:physical_network': None,
|
||||
u'subnets': [u'129f1bc5-4282-4f9d-ae60-4db1e1cac22d',
|
||||
u'0b42e051-5a33-4ac4-9a4f-691d0891d760'],
|
||||
u'updated_at': u'2016-04-23T05:07:06',
|
||||
u'tenant_id': u'f6f6d0b2591f41acb8257656d70029fc',
|
||||
u'created_at': u'2016-04-23T05:07:06',
|
||||
u'tags': [],
|
||||
u'ipv6_address_scope': None,
|
||||
u'provider:segmentation_id': 1057,
|
||||
u'provider:network_type': u'vxlan',
|
||||
u'port_security_enabled': True,
|
||||
u'admin_state_up': True,
|
||||
u'shared': False,
|
||||
u'mtu': 1450,
|
||||
u'id': u'9b68fb64-39d4-4d41-8cc9-f27846c6e5f5',
|
||||
u'name': u'private'},
|
||||
|
||||
{u'provider:physical_network': None,
|
||||
u'ipv6_address_scope': None,
|
||||
u'port_security_enabled': True,
|
||||
u'provider:network_type': u'local',
|
||||
u'id': u'49ef013d-9bb2-4b8f-9eea-e45563efc420',
|
||||
u'router:external': True,
|
||||
u'availability_zone_hints': [],
|
||||
u'availability_zones': [u'nova'],
|
||||
u'ipv4_address_scope': None,
|
||||
u'shared': False,
|
||||
u'status': u'ACTIVE',
|
||||
u'subnets': [u'808c3b3f-3d79-4c5b-a5b6-95dd07abeb2d'],
|
||||
u'description': u'',
|
||||
u'tags': [],
|
||||
u'updated_at': u'2016-04-25T07:14:53',
|
||||
u'is_default': False,
|
||||
u'provider:segmentation_id': None,
|
||||
u'name': u'ext_net',
|
||||
u'admin_state_up': True,
|
||||
u'tenant_id': u'f6f6d0b2591f41acb8257656d70029fc',
|
||||
u'created_at': u'2016-04-25T07:14:53',
|
||||
u'mtu': 1500}
|
||||
]}
|
||||
|
||||
FakeSubnets = {'subnets': [
|
||||
{u'description': u'',
|
||||
u'enable_dhcp': True,
|
||||
u'network_id': u'49ef013d-9bb2-4b8f-9eea-e45563efc420',
|
||||
u'tenant_id': u'f6f6d0b2591f41acb8257656d70029fc',
|
||||
u'created_at': u'2016-04-25T07:15:25',
|
||||
u'dns_nameservers': [],
|
||||
u'updated_at': u'2016-04-25T07:15:25',
|
||||
u'ipv6_ra_mode': None,
|
||||
u'allocation_pools': [{u'start': u'192.168.21.2',
|
||||
u'end': u'192.168.21.254'}],
|
||||
u'gateway_ip': u'192.168.21.1',
|
||||
u'ipv6_address_mode': None,
|
||||
u'ip_version': 4,
|
||||
u'host_routes': [],
|
||||
u'cidr': u'192.168.21.0/24',
|
||||
u'id': u'808c3b3f-3d79-4c5b-a5b6-95dd07abeb2d',
|
||||
u'subnetpool_id': None,
|
||||
u'name': u'ext_subnet'},
|
||||
]}
|
||||
|
||||
FakePorts = {'ports': [
|
||||
{u'allowed_address_pairs': [],
|
||||
u'extra_dhcp_opts': [],
|
||||
u'updated_at': u'2016-04-25T07:15:59',
|
||||
u'device_owner':
|
||||
u'network:router_gateway',
|
||||
u'port_security_enabled': False,
|
||||
u'binding:profile': {},
|
||||
u'fixed_ips': [{u'subnet_id': u'808c3b3f-3d79-4c5b-a5b6-95dd07abeb2d',
|
||||
u'ip_address': u'192.168.21.3'}],
|
||||
u'id': u'2b34c97a-4ccc-44c0-bc50-b7bbfc3508eb',
|
||||
u'security_groups': [],
|
||||
u'binding:vif_details': {},
|
||||
u'binding:vif_type': u'unbound',
|
||||
u'mac_address': u'fa:16:3e:00:47:f2',
|
||||
u'status': u'DOWN',
|
||||
u'binding:host_id': u'',
|
||||
u'description': u'',
|
||||
u'device_id': u'7fc86d4b-4c0e-4ed8-8d39-e27b7c1b7ae8',
|
||||
u'name': u'',
|
||||
u'admin_state_up': True,
|
||||
u'network_id': u'49ef013d-9bb2-4b8f-9eea-e45563efc420',
|
||||
u'dns_name': None,
|
||||
u'created_at': u'2016-04-25T07:15:59',
|
||||
u'binding:vnic_type': u'normal',
|
||||
u'tenant_id': u''},
|
||||
]}
|
||||
|
||||
FakeRoutes = {'routers': [
|
||||
{u'status': u'ACTIVE',
|
||||
u'external_gateway_info':
|
||||
{u'network_id': u'49ef013d-9bb2-4b8f-9eea-e45563efc420',
|
||||
u'enable_snat': True,
|
||||
u'external_fixed_ips':
|
||||
[{u'subnet_id': u'808c3b3f-3d79-4c5b-a5b6-95dd07abeb2d',
|
||||
u'ip_address': u'192.168.21.3'}
|
||||
]},
|
||||
u'availability_zone_hints': [],
|
||||
u'availability_zones': [],
|
||||
u'description': u'',
|
||||
u'admin_state_up': True,
|
||||
u'tenant_id': u'f6f6d0b2591f41acb8257656d70029fc',
|
||||
u'distributed': False,
|
||||
u'routes': [],
|
||||
u'ha': False,
|
||||
u'id': u'7fc86d4b-4c0e-4ed8-8d39-e27b7c1b7ae8',
|
||||
u'name': u'provider_route'}
|
||||
]}
|
||||
|
||||
FakeSecGroup = {'security_groups': [
|
||||
{u'tenant_id': u'23b119d06168447c8dbb4483d9567bd8',
|
||||
u'name': u'default',
|
||||
u'id': u'97910ed4-1dcb-4704-8814-3ddca818ac16',
|
||||
u'description': u'Default security group',
|
||||
u'security_group_rules': [
|
||||
{u'remote_group_id': u'ac4a6134-0176-44db-abab-559d284c4cdc',
|
||||
u'direction': u'ingress',
|
||||
u'protocol': None,
|
||||
u'description': u'',
|
||||
u'ethertype': u'IPv4',
|
||||
u'remote_ip_prefix': None,
|
||||
u'port_range_max': None,
|
||||
u'security_group_id': u'ac4a6134-0176-44db-abab-559d284c4cdc',
|
||||
u'port_range_min': None,
|
||||
u'tenant_id': u'23b119d06168447c8dbb4483d9567bd8',
|
||||
u'id': u'21416a24-6a7a-4830-bbec-1426b21e085a'},
|
||||
|
||||
{u'remote_group_id': u'ac4a6134-0176-44db-abab-559d284c4cdc',
|
||||
u'direction': u'ingress',
|
||||
u'protocol': None,
|
||||
u'description': u'',
|
||||
u'ethertype': u'IPv6',
|
||||
u'remote_ip_prefix': None,
|
||||
u'port_range_max': None,
|
||||
u'security_group_id': u'ac4a6134-0176-44db-abab-559d284c4cdc',
|
||||
u'port_range_min': None,
|
||||
u'tenant_id': u'23b119d06168447c8dbb4483d9567bd8',
|
||||
u'id': u'47f67d6a-4e73-465a-9f4d-d9b850f85f22'},
|
||||
|
||||
{u'remote_group_id': None,
|
||||
u'direction': u'egress',
|
||||
u'protocol': None,
|
||||
u'description': u'',
|
||||
u'ethertype': u'IPv6',
|
||||
u'remote_ip_prefix': None,
|
||||
u'port_range_max': None,
|
||||
u'security_group_id': u'ac4a6134-0176-44db-abab-559d284c4cdc',
|
||||
u'port_range_min': None,
|
||||
u'tenant_id': u'23b119d06168447c8dbb4483d9567bd8',
|
||||
u'id': u'c24e7148-820c-4147-9032-6fcdb96db6f7'}]},
|
||||
]}
|
||||
|
||||
|
||||
def call_hooks(operation, checkpoint, resource, context, parameters, **kwargs):
|
||||
def noop(*args, **kwargs):
|
||||
pass
|
||||
|
||||
hooks = (
|
||||
'on_prepare_begin',
|
||||
'on_prepare_finish',
|
||||
'on_main',
|
||||
'on_complete',
|
||||
)
|
||||
for hook_name in hooks:
|
||||
hook = getattr(operation, hook_name, noop)
|
||||
hook(checkpoint, resource, context, parameters, **kwargs)
|
||||
|
||||
|
||||
class FakeNeutronClient(object):
|
||||
def list_networks(self):
|
||||
return FakeNetworks
|
||||
|
||||
def list_subnets(self):
|
||||
return FakeSubnets
|
||||
|
||||
def list_ports(self):
|
||||
return FakePorts
|
||||
|
||||
def list_routers(self):
|
||||
return FakeRoutes
|
||||
|
||||
def list_security_groups(self):
|
||||
return FakeSecGroup
|
||||
|
||||
|
||||
class FakeBankPlugin(BankPlugin):
|
||||
def __init__(self):
|
||||
self._objects = {}
|
||||
|
||||
def create_object(self, key, value):
|
||||
self._objects[key] = value
|
||||
|
||||
def update_object(self, key, value):
|
||||
self._objects[key] = value
|
||||
|
||||
def get_object(self, key):
|
||||
value = self._objects.get(key, None)
|
||||
if value is None:
|
||||
raise Exception
|
||||
return value
|
||||
|
||||
def list_objects(self, prefix=None, limit=None, marker=None):
|
||||
objects_name = []
|
||||
if prefix is not None:
|
||||
for key, value in self._objects.items():
|
||||
if key.find(prefix) == 0:
|
||||
objects_name.append(key.lstrip(prefix))
|
||||
else:
|
||||
objects_name = self._objects.keys()
|
||||
return objects_name
|
||||
|
||||
def delete_object(self, key):
|
||||
self._objects.pop(key)
|
||||
|
||||
def get_owner_id(self):
|
||||
return
|
||||
|
||||
fake_checkpointid = "checkpoint_id"
|
||||
fake_project_id = "abcd"
|
||||
fake_bank = Bank(FakeBankPlugin())
|
||||
fake_bank_section = BankSection(bank=fake_bank, section="fake")
|
||||
|
||||
ResourceNode = collections.namedtuple(
|
||||
"ResourceNode",
|
||||
["value"]
|
||||
)
|
||||
|
||||
|
||||
class CheckpointCollection(object):
|
||||
def __init__(self):
|
||||
self.bank_section = fake_bank_section
|
||||
|
||||
def get_resource_bank_section(self, resource_id):
|
||||
return self.bank_section
|
||||
|
||||
|
||||
class NeutronProtectionPluginTest(base.TestCase):
|
||||
def setUp(self):
|
||||
super(NeutronProtectionPluginTest, self).setUp()
|
||||
|
||||
self.plugin = NeutronProtectionPlugin()
|
||||
|
||||
cfg.CONF.set_default('neutron_endpoint',
|
||||
'http://127.0.0.1:9696',
|
||||
'neutron_client')
|
||||
|
||||
self.cntxt = RequestContext(user_id='admin',
|
||||
project_id='abcd',
|
||||
auth_token='efgh')
|
||||
|
||||
self.neutron_client = client_factory.ClientFactory.create_client(
|
||||
"neutron", self.cntxt)
|
||||
self.checkpoint = CheckpointCollection()
|
||||
|
||||
def test_get_options_schema(self):
|
||||
options_schema = self.plugin.get_options_schema(
|
||||
'OS::Neutron::Network')
|
||||
self.assertEqual(options_schema,
|
||||
network_plugin_schemas.OPTIONS_SCHEMA)
|
||||
|
||||
def test_get_restore_schema(self):
|
||||
options_schema = self.plugin.get_restore_schema(
|
||||
'OS::Neutron::Network')
|
||||
self.assertEqual(options_schema,
|
||||
network_plugin_schemas.RESTORE_SCHEMA)
|
||||
|
||||
def test_get_saved_info_schema(self):
|
||||
options_schema = self.plugin.get_saved_info_schema(
|
||||
'OS::Neutron::Network')
|
||||
self.assertEqual(options_schema,
|
||||
network_plugin_schemas.SAVED_INFO_SCHEMA)
|
||||
|
||||
def test_get_supported_resources_types(self):
|
||||
types = self.plugin.get_supported_resources_types()
|
||||
self.assertEqual(types,
|
||||
[constants.NETWORK_RESOURCE_TYPE])
|
||||
|
||||
@mock.patch('karbor.services.protection.clients.neutron.create')
|
||||
def test_create_backup(self, mock_neutron_create):
|
||||
resource = Resource(id="network_id_1",
|
||||
type=constants.NETWORK_RESOURCE_TYPE,
|
||||
name="test")
|
||||
|
||||
fake_bank_section.update_object = mock.MagicMock()
|
||||
|
||||
protect_operation = self.plugin.get_protect_operation(resource)
|
||||
mock_neutron_create.return_value = self.neutron_client
|
||||
|
||||
self.neutron_client.list_networks = mock.MagicMock()
|
||||
self.neutron_client.list_networks.return_value = FakeNetworks
|
||||
|
||||
self.neutron_client.list_subnets = mock.MagicMock()
|
||||
self.neutron_client.list_subnets.return_value = FakeSubnets
|
||||
|
||||
self.neutron_client.list_ports = mock.MagicMock()
|
||||
self.neutron_client.list_ports.return_value = FakePorts
|
||||
|
||||
self.neutron_client.list_routers = mock.MagicMock()
|
||||
self.neutron_client.list_routers.return_value = FakeRoutes
|
||||
|
||||
self.neutron_client.list_security_groups = mock.MagicMock()
|
||||
self.neutron_client.list_security_groups.return_value = FakeSecGroup
|
||||
|
||||
call_hooks(protect_operation, self.checkpoint, resource, self.cntxt,
|
||||
{})
|
Loading…
Reference in New Issue