Merge "Allow multiple networks to be created per cluster"
This commit is contained in:
commit
b4f9c65e17
|
@ -25,6 +25,7 @@ from fuelclient.cli.actions.interrupt import ResetAction
|
||||||
from fuelclient.cli.actions.interrupt import StopAction
|
from fuelclient.cli.actions.interrupt import StopAction
|
||||||
from fuelclient.cli.actions.network import NetworkAction
|
from fuelclient.cli.actions.network import NetworkAction
|
||||||
from fuelclient.cli.actions.node import NodeAction
|
from fuelclient.cli.actions.node import NodeAction
|
||||||
|
from fuelclient.cli.actions.nodegroup import NodeGroupAction
|
||||||
from fuelclient.cli.actions.release import ReleaseAction
|
from fuelclient.cli.actions.release import ReleaseAction
|
||||||
from fuelclient.cli.actions.role import RoleAction
|
from fuelclient.cli.actions.role import RoleAction
|
||||||
from fuelclient.cli.actions.settings import SettingsAction
|
from fuelclient.cli.actions.settings import SettingsAction
|
||||||
|
@ -49,7 +50,8 @@ actions_tuple = (
|
||||||
SnapshotAction,
|
SnapshotAction,
|
||||||
HealthCheckAction,
|
HealthCheckAction,
|
||||||
UserAction,
|
UserAction,
|
||||||
PluginAction
|
PluginAction,
|
||||||
|
NodeGroupAction
|
||||||
)
|
)
|
||||||
|
|
||||||
actions = dict(
|
actions = dict(
|
||||||
|
|
|
@ -33,7 +33,7 @@ class NodeAction(Action):
|
||||||
"""
|
"""
|
||||||
action_name = "node"
|
action_name = "node"
|
||||||
acceptable_keys = ("id", "status", "name", "cluster", "ip",
|
acceptable_keys = ("id", "status", "name", "cluster", "ip",
|
||||||
"mac", "roles", "pending_roles", "online")
|
"mac", "roles", "pending_roles", "online", "group_id")
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(NodeAction, self).__init__()
|
super(NodeAction, self).__init__()
|
||||||
|
@ -49,7 +49,7 @@ class NodeAction(Action):
|
||||||
Args.get_delete_from_db_arg(
|
Args.get_delete_from_db_arg(
|
||||||
"Delete specific nodes only from fuel db.\n"
|
"Delete specific nodes only from fuel db.\n"
|
||||||
"User should still delete node from cobbler"),
|
"User should still delete node from cobbler"),
|
||||||
Args.get_provision_arg("Provision specific nodes.")
|
Args.get_provision_arg("Provision specific nodes."),
|
||||||
),
|
),
|
||||||
group(
|
group(
|
||||||
Args.get_default_arg(
|
Args.get_default_arg(
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from fuelclient.cli.actions.base import Action
|
||||||
|
from fuelclient.cli.actions.base import check_all
|
||||||
|
import fuelclient.cli.arguments as Args
|
||||||
|
from fuelclient.cli.arguments import group
|
||||||
|
from fuelclient.cli.error import ActionException
|
||||||
|
from fuelclient.cli.formatting import format_table
|
||||||
|
from fuelclient.objects.node import Node
|
||||||
|
from fuelclient.objects.nodegroup import NodeGroup
|
||||||
|
from fuelclient.objects.nodegroup import NodeGroupCollection
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupAction(Action):
|
||||||
|
"""Show or modify node groups
|
||||||
|
"""
|
||||||
|
action_name = "nodegroup"
|
||||||
|
acceptable_keys = ("id", "cluster", "name")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(NodeGroupAction, self).__init__()
|
||||||
|
self.args = (
|
||||||
|
Args.get_env_arg(),
|
||||||
|
Args.get_list_arg("List all node groups."),
|
||||||
|
Args.get_name_arg("Name of new node group."),
|
||||||
|
Args.get_group_arg("ID of node group."),
|
||||||
|
Args.get_node_arg("List of nodes to assign specified group to."),
|
||||||
|
group(
|
||||||
|
Args.get_create_arg(
|
||||||
|
"Create a new node group in the specified environment."
|
||||||
|
),
|
||||||
|
Args.get_assign_arg(
|
||||||
|
"Download current network configuration."),
|
||||||
|
Args.get_delete_arg(
|
||||||
|
"Verify current network configuration."),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.flag_func_map = (
|
||||||
|
("create", self.create),
|
||||||
|
("delete", self.delete),
|
||||||
|
("assign", self.assign),
|
||||||
|
(None, self.list)
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, params):
|
||||||
|
"""Create a new node group
|
||||||
|
fuel --env 1 nodegroup --create --name "group 1"
|
||||||
|
"""
|
||||||
|
NodeGroup.create(params.name, int(params.env))
|
||||||
|
|
||||||
|
def delete(self, params):
|
||||||
|
"""Delete the specified node groups
|
||||||
|
fuel --env 1 nodegroup --delete --group 1
|
||||||
|
fuel --env 1 nodegroup --delete --group 2,3,4
|
||||||
|
"""
|
||||||
|
ngs = NodeGroup.get_by_ids(params.group)
|
||||||
|
for n in ngs:
|
||||||
|
if n.name == "default":
|
||||||
|
raise ActionException(
|
||||||
|
"Default node groups cannot be deleted."
|
||||||
|
)
|
||||||
|
NodeGroup.delete(n.id)
|
||||||
|
|
||||||
|
@check_all("env")
|
||||||
|
def assign(self, params):
|
||||||
|
"""Assign nodes to specified node group:
|
||||||
|
fuel --env 1 nodegroup --assign --node 1 --group 1
|
||||||
|
fuel --env 1 nodegroup --assign --node 2,3,4 --group 1
|
||||||
|
"""
|
||||||
|
nodes = [n.id for n in map(Node, params.node)]
|
||||||
|
ngs = map(NodeGroup, params.group)
|
||||||
|
if len(ngs) > 1:
|
||||||
|
raise ActionException(
|
||||||
|
"Nodes can only be assigned to one node group."
|
||||||
|
)
|
||||||
|
NodeGroup.assign(ngs[0].id, nodes)
|
||||||
|
|
||||||
|
def list(self, params):
|
||||||
|
"""To list all available node groups:
|
||||||
|
fuel nodegroup
|
||||||
|
|
||||||
|
To filter them by environment:
|
||||||
|
fuel --env-id 1 nodegroup
|
||||||
|
"""
|
||||||
|
group_collection = NodeGroupCollection.get_all()
|
||||||
|
if params.env:
|
||||||
|
group_collection.filter_by_env_id(int(params.env))
|
||||||
|
self.serializer.print_to_output(
|
||||||
|
group_collection.data,
|
||||||
|
format_table(
|
||||||
|
group_collection.data,
|
||||||
|
acceptable_keys=self.acceptable_keys,
|
||||||
|
)
|
||||||
|
)
|
|
@ -337,6 +337,14 @@ def get_delete_arg(help_msg):
|
||||||
return get_boolean_arg("delete", help=help_msg)
|
return get_boolean_arg("delete", help=help_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_assign_arg(help_msg):
|
||||||
|
return get_boolean_arg("assign", help=help_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def get_group_arg(help_msg):
|
||||||
|
return get_set_type_arg("group", help=help_msg)
|
||||||
|
|
||||||
|
|
||||||
def get_release_arg(help_msg, required=False):
|
def get_release_arg(help_msg, required=False):
|
||||||
return get_int_arg(
|
return get_int_arg(
|
||||||
"release",
|
"release",
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
|
from fuelclient.objects.base import BaseObject
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroup(BaseObject):
|
||||||
|
|
||||||
|
class_api_path = "nodegroups/"
|
||||||
|
instance_api_path = "nodegroups/{0}/"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def env_id(self):
|
||||||
|
return self.get_fresh_data()["cluster"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.get_fresh_data()["name"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, name, cluster_id):
|
||||||
|
return cls.connection.post_request(
|
||||||
|
cls.class_api_path,
|
||||||
|
{'cluster_id': cluster_id, 'name': name},
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete(cls, group_id):
|
||||||
|
return cls.connection.delete_request(
|
||||||
|
cls.instance_api_path.format(group_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assign(cls, group_id, nodes):
|
||||||
|
return cls.connection.post_request(
|
||||||
|
cls.instance_api_path.format(group_id),
|
||||||
|
nodes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupCollection(object):
|
||||||
|
|
||||||
|
def __init__(self, groups):
|
||||||
|
self.collection = groups
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_with_ids(cls, ids):
|
||||||
|
return cls(map(NodeGroup, ids))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_with_data(cls, data):
|
||||||
|
return cls(map(NodeGroup.init_with_data, data))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "node groups [{0}]".format(
|
||||||
|
", ".join(map(lambda n: str(n.id), self.collection))
|
||||||
|
)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.collection)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
return map(attrgetter("data"), self.collection)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all(cls):
|
||||||
|
return cls(NodeGroup.get_all())
|
||||||
|
|
||||||
|
def filter_by_env_id(self, env_id):
|
||||||
|
self.collection = filter(
|
||||||
|
lambda group: group.env_id == env_id,
|
||||||
|
self.collection
|
||||||
|
)
|
|
@ -149,10 +149,6 @@ class NeutronNetworkConfigurationHandler(ProviderHandler):
|
||||||
@content_json
|
@content_json
|
||||||
def PUT(self, cluster_id):
|
def PUT(self, cluster_id):
|
||||||
data = jsonutils.loads(web.data())
|
data = jsonutils.loads(web.data())
|
||||||
if data.get("networks"):
|
|
||||||
data["networks"] = [
|
|
||||||
n for n in data["networks"] if n.get("name") != "fuelweb_admin"
|
|
||||||
]
|
|
||||||
cluster = self.get_object_or_404(objects.Cluster, cluster_id)
|
cluster = self.get_object_or_404(objects.Cluster, cluster_id)
|
||||||
self.check_net_provider(cluster)
|
self.check_net_provider(cluster)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ Handlers dealing with nodes
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from netaddr import IPAddress
|
||||||
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
import web
|
import web
|
||||||
|
|
||||||
from nailgun.api.v1.handlers.base import BaseHandler
|
from nailgun.api.v1.handlers.base import BaseHandler
|
||||||
|
@ -44,7 +47,6 @@ from nailgun import notifier
|
||||||
|
|
||||||
|
|
||||||
class NodeHandler(SingleHandler):
|
class NodeHandler(SingleHandler):
|
||||||
|
|
||||||
single = objects.Node
|
single = objects.Node
|
||||||
validator = NodeValidator
|
validator = NodeValidator
|
||||||
|
|
||||||
|
@ -56,7 +58,8 @@ class NodeCollectionHandler(CollectionHandler):
|
||||||
fields = ('id', 'name', 'meta', 'progress', 'roles', 'pending_roles',
|
fields = ('id', 'name', 'meta', 'progress', 'roles', 'pending_roles',
|
||||||
'status', 'mac', 'fqdn', 'ip', 'manufacturer', 'platform_name',
|
'status', 'mac', 'fqdn', 'ip', 'manufacturer', 'platform_name',
|
||||||
'pending_addition', 'pending_deletion', 'os_platform',
|
'pending_addition', 'pending_deletion', 'os_platform',
|
||||||
'error_type', 'online', 'cluster', 'uuid', 'network_data')
|
'error_type', 'online', 'cluster', 'uuid', 'network_data',
|
||||||
|
'group_id')
|
||||||
|
|
||||||
validator = NodeValidator
|
validator = NodeValidator
|
||||||
collection = objects.NodeCollection
|
collection = objects.NodeCollection
|
||||||
|
@ -130,6 +133,16 @@ class NodeAgentHandler(BaseHandler):
|
||||||
raise self.http(404, "Can't find node: {0}".format(nd))
|
raise self.http(404, "Can't find node: {0}".format(nd))
|
||||||
|
|
||||||
node.timestamp = datetime.now()
|
node.timestamp = datetime.now()
|
||||||
|
|
||||||
|
if node.group_id is None:
|
||||||
|
admin_ngs = db().query(NetworkGroup).filter_by(
|
||||||
|
name="fuelweb_admin")
|
||||||
|
ip = IPAddress(node.ip)
|
||||||
|
for ng in admin_ngs:
|
||||||
|
if ip in IPNetwork(ng.cidr):
|
||||||
|
node.group_id = ng.group_id
|
||||||
|
break
|
||||||
|
|
||||||
if not node.online:
|
if not node.online:
|
||||||
node.online = True
|
node.online = True
|
||||||
msg = u"Node '{0}' is back online".format(node.human_readable_name)
|
msg = u"Node '{0}' is back online".format(node.human_readable_name)
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import web
|
||||||
|
|
||||||
|
from nailgun.api.v1.handlers.base import BaseHandler
|
||||||
|
from nailgun.api.v1.handlers.base import CollectionHandler
|
||||||
|
from nailgun.api.v1.handlers.base import SingleHandler
|
||||||
|
|
||||||
|
from nailgun.api.v1.handlers.base import content_json
|
||||||
|
from nailgun.api.v1.validators.node_group import NodeGroupValidator
|
||||||
|
|
||||||
|
from nailgun.db import db
|
||||||
|
|
||||||
|
from nailgun import objects
|
||||||
|
|
||||||
|
"""
|
||||||
|
Handlers dealing with node groups
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupHandler(SingleHandler):
|
||||||
|
"""NodeGroup single handler
|
||||||
|
"""
|
||||||
|
single = objects.NodeGroup
|
||||||
|
validator = NodeGroupValidator
|
||||||
|
|
||||||
|
def DELETE(self, group_id):
|
||||||
|
node_group = self.get_object_or_404(objects.NodeGroup, group_id)
|
||||||
|
db().delete(node_group)
|
||||||
|
db().commit()
|
||||||
|
raise web.webapi.HTTPError(
|
||||||
|
status="204 No Content",
|
||||||
|
data=""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupCollectionHandler(CollectionHandler):
|
||||||
|
"""NodeGroup collection handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
collection = objects.NodeGroupCollection
|
||||||
|
validator = NodeGroupValidator
|
||||||
|
|
||||||
|
@content_json
|
||||||
|
def GET(self):
|
||||||
|
"""May receive cluster_id parameter to filter list
|
||||||
|
of groups
|
||||||
|
|
||||||
|
:returns: Collection of JSONized Task objects.
|
||||||
|
:http: * 200 (OK)
|
||||||
|
* 404 (task not found in db)
|
||||||
|
"""
|
||||||
|
user_data = web.input(cluster_id=None)
|
||||||
|
|
||||||
|
if user_data.cluster_id is not None:
|
||||||
|
return self.collection.to_json(
|
||||||
|
query=self.collection.get_by_cluster_id(
|
||||||
|
user_data.cluster_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self.collection.to_json()
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupAssignmentHandler(BaseHandler):
|
||||||
|
"""Node group assignment handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
@content_json
|
||||||
|
def POST(self, group_id):
|
||||||
|
""":returns: Http response.
|
||||||
|
:http: * 201 (nodes are successfully assigned)
|
||||||
|
* 400 (invalid nodes data specified)
|
||||||
|
"""
|
||||||
|
self.get_object_or_404(
|
||||||
|
objects.NodeGroup,
|
||||||
|
group_id
|
||||||
|
)
|
||||||
|
data = self.checked_data()
|
||||||
|
|
||||||
|
nodes = self.get_objects_list_or_404(
|
||||||
|
objects.NodeCollection,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
objects.Node.update(node, {"group_id": group_id})
|
|
@ -40,6 +40,8 @@ from nailgun.api.v1.handlers.logs import LogEntryCollectionHandler
|
||||||
from nailgun.api.v1.handlers.logs import LogPackageHandler
|
from nailgun.api.v1.handlers.logs import LogPackageHandler
|
||||||
from nailgun.api.v1.handlers.logs import LogSourceByNodeCollectionHandler
|
from nailgun.api.v1.handlers.logs import LogSourceByNodeCollectionHandler
|
||||||
from nailgun.api.v1.handlers.logs import LogSourceCollectionHandler
|
from nailgun.api.v1.handlers.logs import LogSourceCollectionHandler
|
||||||
|
from nailgun.api.v1.handlers.node_group import NodeGroupCollectionHandler
|
||||||
|
from nailgun.api.v1.handlers.node_group import NodeGroupHandler
|
||||||
|
|
||||||
from nailgun.api.v1.handlers.network_configuration \
|
from nailgun.api.v1.handlers.network_configuration \
|
||||||
import NeutronNetworkConfigurationHandler
|
import NeutronNetworkConfigurationHandler
|
||||||
|
@ -146,11 +148,17 @@ urls = (
|
||||||
r'/clusters/(?P<cluster_id>\d+)/update/?$',
|
r'/clusters/(?P<cluster_id>\d+)/update/?$',
|
||||||
ClusterUpdateHandler,
|
ClusterUpdateHandler,
|
||||||
|
|
||||||
|
|
||||||
r'/clusters/(?P<cluster_id>\d+)/assignment/?$',
|
r'/clusters/(?P<cluster_id>\d+)/assignment/?$',
|
||||||
NodeAssignmentHandler,
|
NodeAssignmentHandler,
|
||||||
r'/clusters/(?P<cluster_id>\d+)/unassignment/?$',
|
r'/clusters/(?P<cluster_id>\d+)/unassignment/?$',
|
||||||
NodeUnassignmentHandler,
|
NodeUnassignmentHandler,
|
||||||
|
|
||||||
|
r'/nodegroups/?$',
|
||||||
|
NodeGroupCollectionHandler,
|
||||||
|
r'/nodegroups/(?P<obj_id>\d+)/?$',
|
||||||
|
NodeGroupHandler,
|
||||||
|
|
||||||
r'/nodes/?$',
|
r'/nodes/?$',
|
||||||
NodeCollectionHandler,
|
NodeCollectionHandler,
|
||||||
r'/nodes/agent/?$',
|
r'/nodes/agent/?$',
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nailgun.api.v1.validators.base import BasicValidator
|
||||||
|
from nailgun import consts
|
||||||
|
from nailgun.errors import errors
|
||||||
|
from nailgun import objects
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupValidator(BasicValidator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, data):
|
||||||
|
data = cls.validate_json(data)
|
||||||
|
cluster = objects.Cluster.get_by_uid(data['cluster_id'])
|
||||||
|
if (cluster.net_provider == consts.CLUSTER_NET_PROVIDERS.nova_network
|
||||||
|
or cluster.network_config.segmentation_type !=
|
||||||
|
consts.NEUTRON_SEGMENT_TYPES.gre):
|
||||||
|
raise errors.NotAllowed(
|
||||||
|
"Node groups can only be created when using Neutron GRE."
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_delete(cls, instance, force=False):
|
||||||
|
if (instance.nodes or instance.networks) and not force:
|
||||||
|
raise errors.CannotDelete(
|
||||||
|
"You cannot delete a node group that contains "
|
||||||
|
"nodes or networks"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_update(cls, data, **kwargs):
|
||||||
|
return cls.validate_json(data)
|
|
@ -152,6 +152,21 @@ def upgrade_schema():
|
||||||
['plugin_id'], ['plugins.id'], ondelete='CASCADE'),
|
['plugin_id'], ['plugins.id'], ondelete='CASCADE'),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint('id')
|
||||||
)
|
)
|
||||||
|
op.create_table(
|
||||||
|
'nodegroups',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('cluster_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('name', sa.String(length=50), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['cluster_id'], ['clusters.id']),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_unique_constraint(None, 'clusters', ['name'])
|
||||||
|
op.add_column(
|
||||||
|
'network_groups',
|
||||||
|
sa.Column('group_id', sa.Integer(), nullable=True)
|
||||||
|
)
|
||||||
|
op.drop_column('network_groups', 'cluster_id')
|
||||||
|
op.add_column('nodes', sa.Column('group_id', sa.Integer(), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def upgrade_releases():
|
def upgrade_releases():
|
||||||
|
@ -182,6 +197,7 @@ def upgrade_data():
|
||||||
# do not deploy 5.0.x series
|
# do not deploy 5.0.x series
|
||||||
upgrade_release_set_deployable_false(
|
upgrade_release_set_deployable_false(
|
||||||
connection, ['2014.1', '2014.1.1-5.0.1', '2014.1.1-5.0.2'])
|
connection, ['2014.1', '2014.1.1-5.0.1', '2014.1.1-5.0.2'])
|
||||||
|
upgrade_node_groups(connection)
|
||||||
|
|
||||||
# In Fuel 5.x default releases do not have filled orchestrator_data,
|
# In Fuel 5.x default releases do not have filled orchestrator_data,
|
||||||
# and defaults one have been used. In Fuel 6.0 we're going to change
|
# and defaults one have been used. In Fuel 6.0 we're going to change
|
||||||
|
@ -204,6 +220,45 @@ def downgrade_schema():
|
||||||
map(drop_enum, ENUMS)
|
map(drop_enum, ENUMS)
|
||||||
op.drop_table('cluster_plugins')
|
op.drop_table('cluster_plugins')
|
||||||
op.drop_table('plugins')
|
op.drop_table('plugins')
|
||||||
|
op.drop_column(u'nodes', 'group_id')
|
||||||
|
op.drop_column(u'network_groups', 'group_id')
|
||||||
|
op.add_column(
|
||||||
|
'network_groups',
|
||||||
|
sa.Column('cluster_id', sa.Integer(), sa.ForeignKey('clusters.id'))
|
||||||
|
)
|
||||||
|
op.drop_column(u'releases', 'wizard_metadata')
|
||||||
|
op.drop_table('nodegroups')
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_node_groups(connection):
|
||||||
|
cluster_select = sa.text("SELECT id from clusters")
|
||||||
|
node_sel = sa.text("SELECT id FROM nodes WHERE cluster_id=:cluster_id")
|
||||||
|
node_update = sa.text(
|
||||||
|
"""UPDATE nodes
|
||||||
|
SET group_id=(SELECT id FROM nodegroups WHERE cluster_id=:cluster_id)
|
||||||
|
WHERE id=:id""")
|
||||||
|
group_insert = sa.text("""INSERT INTO nodegroups (cluster_id, name)
|
||||||
|
VALUES(:cluster_id, 'default')""")
|
||||||
|
net_select = sa.text("""SELECT id FROM network_groups WHERE
|
||||||
|
cluster_id=:cluster_id""")
|
||||||
|
net_update = sa.text("""UPDATE network_groups
|
||||||
|
SET group_id=(SELECT id FROM nodegroups WHERE cluster_id=:cluster_id)
|
||||||
|
WHERE id=:id""")
|
||||||
|
|
||||||
|
clusters = connection.execute(cluster_select)
|
||||||
|
|
||||||
|
for cluster in clusters:
|
||||||
|
connection.execute(group_insert, cluster_id=cluster[0])
|
||||||
|
|
||||||
|
# Assign nodes to the newly created node group
|
||||||
|
nodes = connection.execute(node_sel, cluster_id=cluster[0])
|
||||||
|
for node in nodes:
|
||||||
|
connection.execute(node_update, cluster_id=cluster[0], id=node[0])
|
||||||
|
|
||||||
|
# Assign networks to the newly created node group
|
||||||
|
nets = connection.execute(net_select, cluster_id=cluster[0])
|
||||||
|
for net in nets:
|
||||||
|
connection.execute(net_update, cluster_id=cluster[0], id=net[0])
|
||||||
|
|
||||||
|
|
||||||
def downgrade_data():
|
def downgrade_data():
|
||||||
|
|
|
@ -36,7 +36,6 @@ db_str = "{engine}://{user}:{passwd}@{host}:{port}/{name}".format(
|
||||||
|
|
||||||
engine = create_engine(db_str, client_encoding='utf8')
|
engine = create_engine(db_str, client_encoding='utf8')
|
||||||
|
|
||||||
|
|
||||||
class NoCacheQuery(Query):
|
class NoCacheQuery(Query):
|
||||||
"""Override for common Query class.
|
"""Override for common Query class.
|
||||||
Needed for automatic refreshing objects
|
Needed for automatic refreshing objects
|
||||||
|
|
|
@ -31,6 +31,7 @@ from nailgun.db.sqlalchemy.models.node import Role
|
||||||
from nailgun.db.sqlalchemy.models.node import NodeAttributes
|
from nailgun.db.sqlalchemy.models.node import NodeAttributes
|
||||||
from nailgun.db.sqlalchemy.models.node import NodeBondInterface
|
from nailgun.db.sqlalchemy.models.node import NodeBondInterface
|
||||||
from nailgun.db.sqlalchemy.models.node import NodeNICInterface
|
from nailgun.db.sqlalchemy.models.node import NodeNICInterface
|
||||||
|
from nailgun.db.sqlalchemy.models.node import NodeGroup
|
||||||
|
|
||||||
from nailgun.db.sqlalchemy.models.network import NetworkGroup
|
from nailgun.db.sqlalchemy.models.network import NetworkGroup
|
||||||
from nailgun.db.sqlalchemy.models.network import IPAddr
|
from nailgun.db.sqlalchemy.models.network import IPAddr
|
||||||
|
|
|
@ -31,6 +31,7 @@ from nailgun.db import db
|
||||||
from nailgun.db.sqlalchemy.models.base import Base
|
from nailgun.db.sqlalchemy.models.base import Base
|
||||||
from nailgun.db.sqlalchemy.models.fields import JSON
|
from nailgun.db.sqlalchemy.models.fields import JSON
|
||||||
from nailgun.db.sqlalchemy.models.node import Node
|
from nailgun.db.sqlalchemy.models.node import Node
|
||||||
|
from nailgun.db.sqlalchemy.models.node import NodeGroup
|
||||||
|
|
||||||
|
|
||||||
class ClusterChanges(Base):
|
class ClusterChanges(Base):
|
||||||
|
@ -88,17 +89,21 @@ class Cluster(Base):
|
||||||
# During cluster deletion sqlalchemy engine will set null
|
# During cluster deletion sqlalchemy engine will set null
|
||||||
# into cluster foreign key column of notification entity
|
# into cluster foreign key column of notification entity
|
||||||
notifications = relationship("Notification", backref="cluster")
|
notifications = relationship("Notification", backref="cluster")
|
||||||
network_groups = relationship(
|
node_groups = relationship(
|
||||||
"NetworkGroup",
|
"NodeGroup",
|
||||||
backref="cluster",
|
backref="cluster",
|
||||||
cascade="delete",
|
cascade="delete"
|
||||||
order_by="NetworkGroup.id"
|
|
||||||
)
|
)
|
||||||
replaced_deployment_info = Column(JSON, default={})
|
replaced_deployment_info = Column(JSON, default={})
|
||||||
replaced_provisioning_info = Column(JSON, default={})
|
replaced_provisioning_info = Column(JSON, default={})
|
||||||
is_customized = Column(Boolean, default=False)
|
is_customized = Column(Boolean, default=False)
|
||||||
fuel_version = Column(Text, nullable=False)
|
fuel_version = Column(Text, nullable=False)
|
||||||
|
|
||||||
|
def create_default_group(self):
|
||||||
|
ng = NodeGroup(cluster_id=self.id, name="default")
|
||||||
|
db().add(ng)
|
||||||
|
db().commit()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def changes(self):
|
def changes(self):
|
||||||
return [
|
return [
|
||||||
|
@ -128,6 +133,22 @@ class Cluster(Base):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_group(self):
|
||||||
|
if not self.node_groups:
|
||||||
|
self.create_default_group()
|
||||||
|
return [g.id for g in self.node_groups if g.name == "default"][0]
|
||||||
|
|
||||||
|
def get_default_group(self):
|
||||||
|
return [g for g in self.node_groups if g.name == "default"][0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def network_groups(self):
|
||||||
|
net_list = []
|
||||||
|
for ng in self.node_groups:
|
||||||
|
net_list.extend(ng.networks)
|
||||||
|
return net_list
|
||||||
|
|
||||||
|
|
||||||
class Attributes(Base):
|
class Attributes(Base):
|
||||||
__tablename__ = 'attributes'
|
__tablename__ = 'attributes'
|
||||||
|
|
|
@ -65,7 +65,7 @@ class NetworkGroup(Base):
|
||||||
# can be nullable only for fuelweb admin net
|
# can be nullable only for fuelweb admin net
|
||||||
release = Column(Integer, ForeignKey('releases.id'))
|
release = Column(Integer, ForeignKey('releases.id'))
|
||||||
# can be nullable only for fuelweb admin net
|
# can be nullable only for fuelweb admin net
|
||||||
cluster_id = Column(Integer, ForeignKey('clusters.id'))
|
group_id = Column(Integer, ForeignKey('nodegroups.id'), nullable=True)
|
||||||
vlan_start = Column(Integer)
|
vlan_start = Column(Integer)
|
||||||
cidr = Column(String(25))
|
cidr = Column(String(25))
|
||||||
gateway = Column(String(25))
|
gateway = Column(String(25))
|
||||||
|
|
|
@ -67,12 +67,26 @@ class Role(Base):
|
||||||
name = Column(String(50), nullable=False)
|
name = Column(String(50), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroup(Base):
|
||||||
|
__tablename__ = 'nodegroups'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
cluster_id = Column(Integer, ForeignKey('clusters.id'))
|
||||||
|
name = Column(String(50), nullable=False)
|
||||||
|
nodes = relationship("Node")
|
||||||
|
networks = relationship(
|
||||||
|
"NetworkGroup",
|
||||||
|
backref="nodegroup",
|
||||||
|
cascade="delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Node(Base):
|
class Node(Base):
|
||||||
__tablename__ = 'nodes'
|
__tablename__ = 'nodes'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
uuid = Column(String(36), nullable=False,
|
uuid = Column(String(36), nullable=False,
|
||||||
default=lambda: str(uuid.uuid4()), unique=True)
|
default=lambda: str(uuid.uuid4()), unique=True)
|
||||||
cluster_id = Column(Integer, ForeignKey('clusters.id'))
|
cluster_id = Column(Integer, ForeignKey('clusters.id'))
|
||||||
|
group_id = Column(Integer, ForeignKey('nodegroups.id'), nullable=True)
|
||||||
name = Column(Unicode(100))
|
name = Column(Unicode(100))
|
||||||
status = Column(
|
status = Column(
|
||||||
Enum(*consts.NODE_STATUSES, name='node_status'),
|
Enum(*consts.NODE_STATUSES, name='node_status'),
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
"cidr": "{{settings.ADMIN_NETWORK['cidr']}}",
|
"cidr": "{{settings.ADMIN_NETWORK['cidr']}}",
|
||||||
"vlan_start": null,
|
"vlan_start": null,
|
||||||
"meta": {
|
"meta": {
|
||||||
"use_gateway": false,
|
"use_gateway": true,
|
||||||
"notation": "cidr",
|
"notation": "ip_ranges",
|
||||||
"render_type": null,
|
"render_type": null,
|
||||||
"render_addr_mask": null,
|
"render_addr_mask": null,
|
||||||
"map_priority": 0,
|
"map_priority": 0,
|
||||||
|
|
|
@ -66,6 +66,8 @@ class NetworkCheck(object):
|
||||||
if data_net.get('meta'):
|
if data_net.get('meta'):
|
||||||
data_net.pop('meta')
|
data_net.pop('meta')
|
||||||
net.update(data_net)
|
net.update(data_net)
|
||||||
|
if data_net.get('name') == 'fuelweb_admin':
|
||||||
|
net.update(name='admin (PXE)')
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise errors.NetworkCheckError(
|
raise errors.NetworkCheckError(
|
||||||
|
@ -168,8 +170,8 @@ class NetworkCheck(object):
|
||||||
"errors": ["cidr"]
|
"errors": ["cidr"]
|
||||||
})
|
})
|
||||||
# Check for intersection with floating ranges
|
# Check for intersection with floating ranges
|
||||||
nets_w_cidr = filter(lambda n: n['meta']['notation'] == 'cidr',
|
nets_w_cidr = [n for n in self.networks
|
||||||
self.networks)
|
if n.get('cidr') and n['name'] != 'public']
|
||||||
fl_ranges = [netaddr.IPRange(v[0], v[1])
|
fl_ranges = [netaddr.IPRange(v[0], v[1])
|
||||||
for v in self.network_config['floating_ranges']]
|
for v in self.network_config['floating_ranges']]
|
||||||
for net_vs_range in product(nets_w_cidr, fl_ranges):
|
for net_vs_range in product(nets_w_cidr, fl_ranges):
|
||||||
|
|
|
@ -25,6 +25,7 @@ from netaddr import IPRange
|
||||||
|
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from sqlalchemy.sql import not_
|
from sqlalchemy.sql import not_
|
||||||
|
from sqlalchemy.sql import or_
|
||||||
|
|
||||||
from nailgun import objects
|
from nailgun import objects
|
||||||
|
|
||||||
|
@ -36,10 +37,12 @@ from nailgun.db.sqlalchemy.models import NetworkGroup
|
||||||
from nailgun.db.sqlalchemy.models import NetworkNICAssignment
|
from nailgun.db.sqlalchemy.models import NetworkNICAssignment
|
||||||
from nailgun.db.sqlalchemy.models import Node
|
from nailgun.db.sqlalchemy.models import Node
|
||||||
from nailgun.db.sqlalchemy.models import NodeBondInterface
|
from nailgun.db.sqlalchemy.models import NodeBondInterface
|
||||||
|
from nailgun.db.sqlalchemy.models import NodeGroup
|
||||||
from nailgun.db.sqlalchemy.models import NodeNICInterface
|
from nailgun.db.sqlalchemy.models import NodeNICInterface
|
||||||
from nailgun.errors import errors
|
from nailgun.errors import errors
|
||||||
from nailgun.logger import logger
|
from nailgun.logger import logger
|
||||||
from nailgun.utils.zabbix import ZabbixManager
|
from nailgun.utils.zabbix import ZabbixManager
|
||||||
|
from nailgun.settings import settings
|
||||||
|
|
||||||
|
|
||||||
class NetworkManager(object):
|
class NetworkManager(object):
|
||||||
|
@ -61,26 +64,33 @@ class NetworkManager(object):
|
||||||
db().commit()
|
db().commit()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_admin_network_group_id(cls):
|
def get_admin_network_group_id(cls, node_id=None):
|
||||||
"""Method for receiving Admin NetworkGroup ID.
|
"""Method for receiving Admin NetworkGroup ID.
|
||||||
|
|
||||||
:type fail_if_not_found: bool
|
:type fail_if_not_found: bool
|
||||||
:returns: Admin NetworkGroup ID or None.
|
:returns: Admin NetworkGroup ID or None.
|
||||||
:raises: errors.AdminNetworkNotFound
|
:raises: errors.AdminNetworkNotFound
|
||||||
"""
|
"""
|
||||||
return cls.get_admin_network_group().id
|
return cls.get_admin_network_group(node_id=node_id).id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_admin_network_group(cls):
|
def get_admin_network_group(cls, node_id=None):
|
||||||
"""Method for receiving Admin NetworkGroup.
|
"""Method for receiving Admin NetworkGroup.
|
||||||
|
|
||||||
:type fail_if_not_found: bool
|
:type fail_if_not_found: bool
|
||||||
:returns: Admin NetworkGroup or None.
|
:returns: Admin NetworkGroup or None.
|
||||||
:raises: errors.AdminNetworkNotFound
|
:raises: errors.AdminNetworkNotFound
|
||||||
"""
|
"""
|
||||||
admin_ng = db().query(NetworkGroup).filter_by(
|
admin_ng = None
|
||||||
name="fuelweb_admin"
|
admin_ngs = db().query(NetworkGroup).filter_by(
|
||||||
).first()
|
name="fuelweb_admin",
|
||||||
|
)
|
||||||
|
if node_id:
|
||||||
|
node_db = db().query(Node).get(node_id)
|
||||||
|
admin_ng = admin_ngs.filter_by(group_id=node_db.group_id).first()
|
||||||
|
|
||||||
|
admin_ng = admin_ng or admin_ngs.filter_by(group_id=None).first()
|
||||||
|
|
||||||
if not admin_ng:
|
if not admin_ng:
|
||||||
raise errors.AdminNetworkNotFound()
|
raise errors.AdminNetworkNotFound()
|
||||||
return admin_ng
|
return admin_ng
|
||||||
|
@ -112,13 +122,12 @@ class NetworkManager(object):
|
||||||
:type num: int
|
:type num: int
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
admin_net_id = cls.get_admin_network_group_id()
|
|
||||||
admin_net = db().query(NetworkGroup).get(admin_net_id)
|
|
||||||
|
|
||||||
# Check which nodes need ips
|
# Check which nodes need ips
|
||||||
nodes_need_ips = []
|
nodes_need_ips = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
node_id = node.id
|
node_id = node.id
|
||||||
|
admin_net = cls.get_admin_network_group(node_id)
|
||||||
|
admin_net_id = admin_net.id
|
||||||
node_admin_ips = db().query(IPAddr).filter_by(
|
node_admin_ips = db().query(IPAddr).filter_by(
|
||||||
node=node_id, network=admin_net_id)
|
node=node_id, network=admin_net_id)
|
||||||
logger.debug(u"Trying to assign admin ip: node=%s", node_id)
|
logger.debug(u"Trying to assign admin ip: node=%s", node_id)
|
||||||
|
@ -161,11 +170,10 @@ class NetworkManager(object):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
network = db().query(NetworkGroup).\
|
network_groups = db().query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster_id).\
|
filter_by(name=network_name)
|
||||||
filter_by(name=network_name).first()
|
|
||||||
|
|
||||||
if not network:
|
if not network_groups:
|
||||||
raise errors.AssignIPError(
|
raise errors.AssignIPError(
|
||||||
u"Network '%s' for cluster_id=%s not found." %
|
u"Network '%s' for cluster_id=%s not found." %
|
||||||
(network_name, cluster_id)
|
(network_name, cluster_id)
|
||||||
|
@ -179,6 +187,14 @@ class NetworkManager(object):
|
||||||
if network_name == 'public' and \
|
if network_name == 'public' and \
|
||||||
not objects.Node.should_have_public(node):
|
not objects.Node.should_have_public(node):
|
||||||
continue
|
continue
|
||||||
|
group_id = node.group_id or cluster.default_group
|
||||||
|
|
||||||
|
network = network_groups.filter(
|
||||||
|
or_(
|
||||||
|
NetworkGroup.group_id == group_id,
|
||||||
|
NetworkGroup.group_id == None # flake8: noqa
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
|
||||||
node_ips = imap(
|
node_ips = imap(
|
||||||
lambda i: i.ip_addr,
|
lambda i: i.ip_addr,
|
||||||
|
@ -248,14 +264,22 @@ class NetworkManager(object):
|
||||||
if not cluster:
|
if not cluster:
|
||||||
raise Exception(u"Cluster id='%s' not found" % cluster_id)
|
raise Exception(u"Cluster id='%s' not found" % cluster_id)
|
||||||
|
|
||||||
|
group_id = None
|
||||||
|
for node in cluster.nodes:
|
||||||
|
if 'controller' in node.all_roles or \
|
||||||
|
'primary-controller' in node.all_roles:
|
||||||
|
group_id = node.group_id
|
||||||
|
break
|
||||||
|
|
||||||
|
if not group_id:
|
||||||
|
group_id = cluster.default_group
|
||||||
|
|
||||||
network = db().query(NetworkGroup).\
|
network = db().query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster_id).\
|
filter_by(name=network_name, group_id=group_id).first()
|
||||||
filter_by(name=network_name).first()
|
|
||||||
|
|
||||||
if not network:
|
if not network:
|
||||||
raise Exception(u"Network '%s' for cluster_id=%s not found." %
|
raise Exception(u"Network '%s' for cluster_id=%s not found." %
|
||||||
(network_name, cluster_id))
|
(network_name, cluster_id))
|
||||||
|
|
||||||
admin_net_id = cls.get_admin_network_group_id()
|
admin_net_id = cls.get_admin_network_group_id()
|
||||||
cluster_ips = [ne.ip_addr for ne in db().query(IPAddr).filter_by(
|
cluster_ips = [ne.ip_addr for ne in db().query(IPAddr).filter_by(
|
||||||
network=network.id,
|
network=network.id,
|
||||||
|
@ -365,7 +389,7 @@ class NetworkManager(object):
|
||||||
ips = ips.filter_by(network=network_id)
|
ips = ips.filter_by(network=network_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
admin_net_id = cls.get_admin_network_group_id()
|
admin_net_id = cls.get_admin_network_group_id(node_id=node_id)
|
||||||
except errors.AdminNetworkNotFound:
|
except errors.AdminNetworkNotFound:
|
||||||
admin_net_id = None
|
admin_net_id = None
|
||||||
if admin_net_id:
|
if admin_net_id:
|
||||||
|
@ -393,7 +417,12 @@ class NetworkManager(object):
|
||||||
networks metadata
|
networks metadata
|
||||||
"""
|
"""
|
||||||
nics = []
|
nics = []
|
||||||
ngs = node.cluster.network_groups + [cls.get_admin_network_group()]
|
group_id = node.group_id
|
||||||
|
if not group_id:
|
||||||
|
group_id = node.cluster.default_group
|
||||||
|
|
||||||
|
node_group = db().query(NodeGroup).get(group_id)
|
||||||
|
ngs = node_group.networks + [cls.get_admin_network_group(node.id)]
|
||||||
ngs_by_id = dict((ng.id, ng) for ng in ngs)
|
ngs_by_id = dict((ng.id, ng) for ng in ngs)
|
||||||
# sort Network Groups ids by map_priority
|
# sort Network Groups ids by map_priority
|
||||||
to_assign_ids = list(
|
to_assign_ids = list(
|
||||||
|
@ -403,7 +432,7 @@ class NetworkManager(object):
|
||||||
key=lambda x: x[1]))[0]
|
key=lambda x: x[1]))[0]
|
||||||
)
|
)
|
||||||
ng_ids = set(ng.id for ng in ngs)
|
ng_ids = set(ng.id for ng in ngs)
|
||||||
ng_wo_admin_ids = ng_ids ^ set([cls.get_admin_network_group_id()])
|
ng_wo_admin_ids = ng_ids ^ set([cls.get_admin_network_group_id(node.id)])
|
||||||
for nic in node.nic_interfaces:
|
for nic in node.nic_interfaces:
|
||||||
nic_dict = {
|
nic_dict = {
|
||||||
"id": nic.id,
|
"id": nic.id,
|
||||||
|
@ -483,7 +512,14 @@ class NetworkManager(object):
|
||||||
:type node: Node
|
:type node: Node
|
||||||
:returns: List of network groups for cluster node belongs to.
|
:returns: List of network groups for cluster node belongs to.
|
||||||
"""
|
"""
|
||||||
return node.cluster.network_groups
|
if node.group_id:
|
||||||
|
return db().query(NetworkGroup).filter_by(
|
||||||
|
group_id=node.group_id,
|
||||||
|
).filter(
|
||||||
|
NetworkGroup.name != 'fuelweb_admin'
|
||||||
|
).order_by(NetworkGroup.id).all()
|
||||||
|
else:
|
||||||
|
return node.cluster.network_groups
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node_networkgroups_ids(cls, node):
|
def get_node_networkgroups_ids(cls, node):
|
||||||
|
@ -494,23 +530,27 @@ class NetworkManager(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_admin_node_network(cls, node):
|
def _get_admin_node_network(cls, node):
|
||||||
net = cls.get_admin_network_group()
|
node_db = db().query(Node).get(node)
|
||||||
|
net = cls.get_admin_network_group(node)
|
||||||
net_cidr = IPNetwork(net.cidr)
|
net_cidr = IPNetwork(net.cidr)
|
||||||
ip_addr = cls.get_admin_ip_for_node(node)
|
ip_addr = cls.get_admin_ip_for_node(node)
|
||||||
|
if ip_addr:
|
||||||
|
ip_addr = "{0}/{1}".format(ip_addr, net_cidr.prefixlen)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name': net.name,
|
'name': net.name,
|
||||||
|
'cidr': net.cidr,
|
||||||
'vlan': net.vlan_start,
|
'vlan': net.vlan_start,
|
||||||
'ip': "{0}/{1}".format(ip_addr, net_cidr.prefixlen),
|
'ip': ip_addr,
|
||||||
'netmask': str(net_cidr.netmask),
|
'netmask': str(net_cidr.netmask),
|
||||||
'brd': str(net_cidr.broadcast),
|
'brd': str(net_cidr.broadcast),
|
||||||
'gateway': net.gateway,
|
'gateway': net.gateway,
|
||||||
'dev': node.admin_interface.name
|
'dev': node_db.admin_interface.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node_network_by_netname(cls, node, netname):
|
def get_node_network_by_netname(cls, node, netname):
|
||||||
networks = cls.get_node_networks(node)
|
networks = cls.get_node_networks(node)
|
||||||
networks.append(cls._get_admin_node_network(node))
|
|
||||||
return filter(
|
return filter(
|
||||||
lambda n: n['name'] == netname, networks)[0]
|
lambda n: n['name'] == netname, networks)[0]
|
||||||
|
|
||||||
|
@ -529,6 +569,7 @@ class NetworkManager(object):
|
||||||
prefix = str(IPNetwork(net.cidr).prefixlen)
|
prefix = str(IPNetwork(net.cidr).prefixlen)
|
||||||
return {
|
return {
|
||||||
'name': net.name,
|
'name': net.name,
|
||||||
|
'cidr': net.cidr,
|
||||||
'vlan': cls.get_network_vlan(net, node_db.cluster),
|
'vlan': cls.get_network_vlan(net, node_db.cluster),
|
||||||
'ip': ip.ip_addr + '/' + prefix,
|
'ip': ip.ip_addr + '/' + prefix,
|
||||||
'netmask': str(IPNetwork(net.cidr).netmask),
|
'netmask': str(IPNetwork(net.cidr).netmask),
|
||||||
|
@ -539,6 +580,7 @@ class NetworkManager(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_network_data_wo_ip(cls, node_db, interface, net):
|
def _get_network_data_wo_ip(cls, node_db, interface, net):
|
||||||
return {'name': net.name,
|
return {'name': net.name,
|
||||||
|
'cidr': net.cidr,
|
||||||
'vlan': cls.get_network_vlan(net, node_db.cluster),
|
'vlan': cls.get_network_vlan(net, node_db.cluster),
|
||||||
'dev': interface.name}
|
'dev': interface.name}
|
||||||
|
|
||||||
|
@ -548,27 +590,27 @@ class NetworkManager(object):
|
||||||
if net.name != 'fuelweb_admin')
|
if net.name != 'fuelweb_admin')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node_networks(cls, node_db):
|
def get_node_networks(cls, node):
|
||||||
cluster_db = node_db.cluster
|
cluster_db = node.cluster
|
||||||
if cluster_db is None:
|
if cluster_db is None:
|
||||||
# Node doesn't belong to any cluster, so it should not have nets
|
# Node doesn't belong to any cluster, so it should not have nets
|
||||||
return []
|
return []
|
||||||
|
|
||||||
network_data = []
|
network_data = []
|
||||||
for interface in node_db.interfaces:
|
for interface in node.interfaces:
|
||||||
networks_wo_admin = cls._get_networks_except_admin(
|
networks_wo_admin = cls._get_networks_except_admin(
|
||||||
interface.assigned_networks_list)
|
interface.assigned_networks_list)
|
||||||
for net in networks_wo_admin:
|
for net in networks_wo_admin:
|
||||||
ip = cls._get_ip_by_network_name(node_db, net.name)
|
ip = cls._get_ip_by_network_name(node, net.name)
|
||||||
if ip is not None:
|
if ip is not None:
|
||||||
network_data.append(cls._get_network_data_with_ip(
|
network_data.append(cls._get_network_data_with_ip(
|
||||||
node_db, interface, net, ip))
|
node, interface, net, ip))
|
||||||
else:
|
else:
|
||||||
if not cls.fixed_and_vlan_manager(net, cluster_db):
|
if not cls.fixed_and_vlan_manager(net, cluster_db):
|
||||||
network_data.append(cls._get_network_data_wo_ip(
|
network_data.append(cls._get_network_data_wo_ip(
|
||||||
node_db, interface, net))
|
node, interface, net))
|
||||||
|
|
||||||
network_data.append(cls._get_admin_network(node_db))
|
network_data.append(cls._get_admin_node_network(node.id))
|
||||||
|
|
||||||
return network_data
|
return network_data
|
||||||
|
|
||||||
|
@ -678,7 +720,7 @@ class NetworkManager(object):
|
||||||
admin_interface = None
|
admin_interface = None
|
||||||
for interface in interfaces:
|
for interface in interfaces:
|
||||||
ip_addr = interface.get('ip')
|
ip_addr = interface.get('ip')
|
||||||
if cls.is_ip_belongs_to_admin_subnet(ip_addr):
|
if cls.is_ip_belongs_to_admin_subnet(ip_addr, node.id):
|
||||||
# Interface was founded
|
# Interface was founded
|
||||||
admin_interface = interface
|
admin_interface = interface
|
||||||
break
|
break
|
||||||
|
@ -702,8 +744,8 @@ class NetworkManager(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_ip_belongs_to_admin_subnet(cls, ip_addr):
|
def is_ip_belongs_to_admin_subnet(cls, ip_addr, node_id=None):
|
||||||
admin_cidr = cls.get_admin_network_group().cidr
|
admin_cidr = cls.get_admin_network_group(node_id).cidr
|
||||||
if ip_addr and IPAddress(ip_addr) in IPNetwork(admin_cidr):
|
if ip_addr and IPAddress(ip_addr) in IPNetwork(admin_cidr):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -760,25 +802,26 @@ class NetworkManager(object):
|
||||||
def get_admin_ip_for_node(cls, node):
|
def get_admin_ip_for_node(cls, node):
|
||||||
"""Returns first admin IP address for node
|
"""Returns first admin IP address for node
|
||||||
"""
|
"""
|
||||||
admin_net_id = cls.get_admin_network_group_id()
|
admin_net_id = cls.get_admin_network_group_id(node_id=node)
|
||||||
admin_ip = db().query(IPAddr).order_by(
|
admin_ip = db().query(IPAddr).order_by(
|
||||||
IPAddr.id
|
IPAddr.id
|
||||||
).filter_by(
|
).filter_by(
|
||||||
node=node.id
|
node=node
|
||||||
).filter_by(
|
).filter_by(
|
||||||
network=admin_net_id
|
network=admin_net_id
|
||||||
).first()
|
).first()
|
||||||
return admin_ip.ip_addr
|
|
||||||
|
return getattr(admin_ip, 'ip_addr', None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_admin_ips_for_interfaces(cls, node):
|
def get_admin_ips_for_interfaces(cls, node):
|
||||||
"""Returns mapping admin {"inteface name" => "admin ip"}
|
"""Returns mapping admin {"inteface name" => "admin ip"}
|
||||||
"""
|
"""
|
||||||
admin_net_id = cls.get_admin_network_group_id()
|
admin_net_id = cls.get_admin_network_group_id(node)
|
||||||
admin_ips = set([
|
admin_ips = set([
|
||||||
i.ip_addr for i in db().query(IPAddr).
|
i.ip_addr for i in db().query(IPAddr).
|
||||||
order_by(IPAddr.id).
|
order_by(IPAddr.id).
|
||||||
filter_by(node=node.id).
|
filter_by(node=node).
|
||||||
filter_by(network=admin_net_id)])
|
filter_by(network=admin_net_id)])
|
||||||
|
|
||||||
interfaces_names = sorted(set([
|
interfaces_names = sorted(set([
|
||||||
|
@ -789,8 +832,14 @@ class NetworkManager(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_admin_network(cls, node):
|
def _get_admin_network(cls, node):
|
||||||
"""Returns dict with admin network."""
|
"""Returns dict with admin network."""
|
||||||
|
|
||||||
|
net = cls.get_admin_network_group(node_id=node)
|
||||||
return {
|
return {
|
||||||
'name': 'admin',
|
'id': net.id,
|
||||||
|
'cidr': net.cidr,
|
||||||
|
'name': net.name,
|
||||||
|
'gateway': net.gateway,
|
||||||
|
'vlan': net.vlan_start,
|
||||||
'dev': cls.get_admin_interface(node).name
|
'dev': cls.get_admin_interface(node).name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,7 +879,8 @@ class NetworkManager(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_ip_by_network_name(cls, node, network_name):
|
def _get_ip_by_network_name(cls, node, network_name):
|
||||||
for ip in node.ip_addrs:
|
for ip in node.ip_addrs:
|
||||||
if ip.network_data.name == network_name:
|
ng = ip.network_data
|
||||||
|
if ng.name == network_name and ng.group_id == node.group_id:
|
||||||
return ip
|
return ip
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -937,7 +987,23 @@ class NetworkManager(object):
|
||||||
db().commit()
|
db().commit()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_network_groups(cls, cluster_id, neutron_segment_type):
|
def create_admin_network_group(cls, cluster_id, group_id):
|
||||||
|
cluster_db = objects.Cluster.get_by_uid(cluster_id)
|
||||||
|
admin_ng = cls.get_admin_network_group()
|
||||||
|
new_admin = NetworkGroup(
|
||||||
|
release=cluster_db.release.id,
|
||||||
|
name='fuelweb_admin',
|
||||||
|
cidr='9.9.9.0/24',
|
||||||
|
gateway='9.9.9.1',
|
||||||
|
group_id=group_id,
|
||||||
|
vlan_start=None,
|
||||||
|
meta=admin_ng.meta
|
||||||
|
)
|
||||||
|
db().add(new_admin)
|
||||||
|
db().flush()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_network_groups(cls, cluster_id, neutron_segment_type, gid=None):
|
||||||
"""Method for creation of network groups for cluster.
|
"""Method for creation of network groups for cluster.
|
||||||
|
|
||||||
:param cluster_id: Cluster database ID.
|
:param cluster_id: Cluster database ID.
|
||||||
|
@ -945,6 +1011,7 @@ class NetworkManager(object):
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
cluster_db = objects.Cluster.get_by_uid(cluster_id)
|
cluster_db = objects.Cluster.get_by_uid(cluster_id)
|
||||||
|
group_id = gid or cluster_db.default_group
|
||||||
networks_metadata = cluster_db.release.networks_metadata
|
networks_metadata = cluster_db.release.networks_metadata
|
||||||
networks_list = networks_metadata[cluster_db.net_provider]["networks"]
|
networks_list = networks_metadata[cluster_db.net_provider]["networks"]
|
||||||
used_nets = [IPNetwork(cls.get_admin_network_group().cidr)]
|
used_nets = [IPNetwork(cls.get_admin_network_group().cidr)]
|
||||||
|
@ -992,7 +1059,7 @@ class NetworkManager(object):
|
||||||
name=net['name'],
|
name=net['name'],
|
||||||
cidr=str(cidr) if cidr else None,
|
cidr=str(cidr) if cidr else None,
|
||||||
gateway=gw,
|
gateway=gw,
|
||||||
cluster_id=cluster_id,
|
group_id=group_id,
|
||||||
vlan_start=vlan_start,
|
vlan_start=vlan_start,
|
||||||
meta=net
|
meta=net
|
||||||
)
|
)
|
||||||
|
@ -1025,7 +1092,7 @@ class NetworkManager(object):
|
||||||
|
|
||||||
if ng_db.meta.get("notation"):
|
if ng_db.meta.get("notation"):
|
||||||
cls.cleanup_network_group(ng_db)
|
cls.cleanup_network_group(ng_db)
|
||||||
objects.Cluster.add_pending_changes(ng_db.cluster, 'networks')
|
objects.Cluster.add_pending_changes(cluster, 'networks')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update(cls, cluster, network_configuration):
|
def update(cls, cluster, network_configuration):
|
||||||
|
@ -1054,3 +1121,28 @@ class NetworkManager(object):
|
||||||
data.get('net_l23_provider'))
|
data.get('net_l23_provider'))
|
||||||
elif cluster.net_provider == 'nova_network':
|
elif cluster.net_provider == 'nova_network':
|
||||||
cls.create_nova_network_config(cluster)
|
cls.create_nova_network_config(cluster)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_gateway(cls, node_id):
|
||||||
|
return cls.get_admin_network_group(node_id).gateway \
|
||||||
|
or settings.MASTER_IP
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_networks_not_on_node(cls, node):
|
||||||
|
node_net = [(n['name'], n['cidr'])
|
||||||
|
for n in cls.get_node_networks(node) if n.get('cidr')]
|
||||||
|
all_nets = [(n.name, n.cidr)
|
||||||
|
for n in node.cluster.network_groups if n.cidr]
|
||||||
|
|
||||||
|
if node.group_id != node.cluster.default_group:
|
||||||
|
admin_net = cls.get_admin_network_group()
|
||||||
|
all_nets.append((admin_net.name, admin_net.cidr))
|
||||||
|
|
||||||
|
other_nets = set(all_nets) ^ set(node_net)
|
||||||
|
output = {}
|
||||||
|
for name, cidr in other_nets:
|
||||||
|
if name not in output:
|
||||||
|
output[name] = []
|
||||||
|
output[name].append(cidr)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
|
@ -41,5 +41,8 @@ from nailgun.objects.capacity import CapacityLog
|
||||||
|
|
||||||
from nailgun.objects.master_node_settings import MasterNodeSettings
|
from nailgun.objects.master_node_settings import MasterNodeSettings
|
||||||
|
|
||||||
|
from nailgun.objects.node_group import NodeGroup
|
||||||
|
from nailgun.objects.node_group import NodeGroupCollection
|
||||||
|
|
||||||
from nailgun.objects.plugin import Plugin
|
from nailgun.objects.plugin import Plugin
|
||||||
from nailgun.objects.plugin import PluginCollection
|
from nailgun.objects.plugin import PluginCollection
|
||||||
|
|
|
@ -173,6 +173,7 @@ class Cluster(NailgunObject):
|
||||||
|
|
||||||
data["fuel_version"] = settings.VERSION["release"]
|
data["fuel_version"] = settings.VERSION["release"]
|
||||||
new_cluster = super(Cluster, cls).create(data)
|
new_cluster = super(Cluster, cls).create(data)
|
||||||
|
new_cluster.create_default_group()
|
||||||
|
|
||||||
cls.create_attributes(new_cluster)
|
cls.create_attributes(new_cluster)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ import traceback
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from netaddr import IPAddress
|
||||||
|
from netaddr import IPNetwork
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from sqlalchemy.orm import subqueryload_all
|
from sqlalchemy.orm import subqueryload_all
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ class Node(NailgunObject):
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": list(consts.NODE_STATUSES)
|
"enum": list(consts.NODE_STATUSES)
|
||||||
},
|
},
|
||||||
|
"group_id": {"type": "number"},
|
||||||
"meta": {"type": "object"},
|
"meta": {"type": "object"},
|
||||||
"mac": {"type": "string"},
|
"mac": {"type": "string"},
|
||||||
"fqdn": {"type": "string"},
|
"fqdn": {"type": "string"},
|
||||||
|
@ -222,6 +225,24 @@ class Node(NailgunObject):
|
||||||
cls.create_discover_notification(new_node)
|
cls.create_discover_notification(new_node)
|
||||||
return new_node
|
return new_node
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assign_group(cls, instance):
|
||||||
|
if instance.group_id is None and instance.ip:
|
||||||
|
admin_ngs = db().query(models.NetworkGroup).filter_by(
|
||||||
|
name="fuelweb_admin")
|
||||||
|
ip = IPAddress(instance.ip)
|
||||||
|
|
||||||
|
for ng in admin_ngs:
|
||||||
|
if ip in IPNetwork(ng.cidr):
|
||||||
|
instance.group_id = ng.group_id
|
||||||
|
break
|
||||||
|
|
||||||
|
if not instance.group_id:
|
||||||
|
instance.group_id = instance.cluster.default_group
|
||||||
|
|
||||||
|
db().add(instance)
|
||||||
|
db().flush()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_attributes(cls, instance):
|
def create_attributes(cls, instance):
|
||||||
"""Create attributes for Node instance
|
"""Create attributes for Node instance
|
||||||
|
@ -401,6 +422,10 @@ class Node(NailgunObject):
|
||||||
|
|
||||||
if new_meta:
|
if new_meta:
|
||||||
instance.update_meta(new_meta)
|
instance.update_meta(new_meta)
|
||||||
|
# The call to update_interfaces will execute a select query for
|
||||||
|
# the current instance. This appears to overwrite the object in the
|
||||||
|
# current session and we lose the meta changes.
|
||||||
|
db().flush()
|
||||||
# smarter check needed
|
# smarter check needed
|
||||||
cls.update_interfaces(instance)
|
cls.update_interfaces(instance)
|
||||||
|
|
||||||
|
@ -424,6 +449,15 @@ class Node(NailgunObject):
|
||||||
cluster_changed = True
|
cluster_changed = True
|
||||||
cls.add_into_cluster(instance, new_cluster_id)
|
cls.add_into_cluster(instance, new_cluster_id)
|
||||||
|
|
||||||
|
if "group_id" in data:
|
||||||
|
new_group_id = data.pop("group_id")
|
||||||
|
if instance.group_id != new_group_id:
|
||||||
|
nm = Cluster.get_network_manager(instance.cluster)
|
||||||
|
nm.clear_assigned_networks(instance)
|
||||||
|
nm.clear_bond_configuration(instance)
|
||||||
|
instance.group_id = new_group_id
|
||||||
|
cls.add_into_cluster(instance, instance.cluster_id)
|
||||||
|
|
||||||
# calculating flags
|
# calculating flags
|
||||||
roles_changed = (
|
roles_changed = (
|
||||||
roles is not None and set(roles) != set(instance.roles)
|
roles is not None and set(roles) != set(instance.roles)
|
||||||
|
@ -491,7 +525,7 @@ class Node(NailgunObject):
|
||||||
#(dshulyak) change this verification to NODE_STATUSES.deploying
|
#(dshulyak) change this verification to NODE_STATUSES.deploying
|
||||||
# after we will reuse ips from dhcp range
|
# after we will reuse ips from dhcp range
|
||||||
netmanager = Cluster.get_network_manager()
|
netmanager = Cluster.get_network_manager()
|
||||||
admin_ng = netmanager.get_admin_network_group()
|
admin_ng = netmanager.get_admin_network_group(instance.id)
|
||||||
if data.get('ip') and not netmanager.is_same_network(data['ip'],
|
if data.get('ip') and not netmanager.is_same_network(data['ip'],
|
||||||
admin_ng.cidr):
|
admin_ng.cidr):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -582,7 +616,7 @@ class Node(NailgunObject):
|
||||||
"""
|
"""
|
||||||
instance.cluster_id = cluster_id
|
instance.cluster_id = cluster_id
|
||||||
db().flush()
|
db().flush()
|
||||||
|
cls.assign_group(instance)
|
||||||
network_manager = Cluster.get_network_manager(instance.cluster)
|
network_manager = Cluster.get_network_manager(instance.cluster)
|
||||||
network_manager.assign_networks_by_default(instance)
|
network_manager.assign_networks_by_default(instance)
|
||||||
cls.add_pending_change(instance, consts.CLUSTER_CHANGES.interfaces)
|
cls.add_pending_change(instance, consts.CLUSTER_CHANGES.interfaces)
|
||||||
|
@ -638,6 +672,7 @@ class Node(NailgunObject):
|
||||||
cls.update_pending_roles(instance, [])
|
cls.update_pending_roles(instance, [])
|
||||||
cls.remove_replaced_params(instance)
|
cls.remove_replaced_params(instance)
|
||||||
instance.cluster_id = None
|
instance.cluster_id = None
|
||||||
|
instance.group_id = None
|
||||||
instance.kernel_params = None
|
instance.kernel_params = None
|
||||||
instance.reset_name_to_default()
|
instance.reset_name_to_default()
|
||||||
db().flush()
|
db().flush()
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from nailgun.objects.serializers.node_group import NodeGroupSerializer
|
||||||
|
|
||||||
|
from nailgun.db import db
|
||||||
|
from nailgun.db.sqlalchemy.models import NodeGroup as DBNodeGroup
|
||||||
|
from nailgun.errors import errors
|
||||||
|
from nailgun.objects import Cluster
|
||||||
|
from nailgun.objects import NailgunCollection
|
||||||
|
from nailgun.objects import NailgunObject
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroup(NailgunObject):
|
||||||
|
|
||||||
|
model = DBNodeGroup
|
||||||
|
serializer = NodeGroupSerializer
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"title": "NodeGroup",
|
||||||
|
"description": "Serialized NodeGroup object",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"type": "number"},
|
||||||
|
"cluster_id": {"type": "number"},
|
||||||
|
"name": {"type": "string"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, data):
|
||||||
|
new_group = super(NodeGroup, cls).create(data)
|
||||||
|
try:
|
||||||
|
cluster = Cluster.get_by_uid(new_group.cluster_id)
|
||||||
|
nm = Cluster.get_network_manager(cluster)
|
||||||
|
nst = cluster.network_config.segmentation_type
|
||||||
|
nm.create_network_groups(new_group.cluster_id, nst,
|
||||||
|
gid=new_group.id)
|
||||||
|
nm.create_admin_network_group(new_group.cluster_id, new_group.id)
|
||||||
|
except (
|
||||||
|
errors.OutOfVLANs,
|
||||||
|
errors.OutOfIPs,
|
||||||
|
errors.NoSuitableCIDR,
|
||||||
|
errors.InvalidNetworkPool
|
||||||
|
) as exc:
|
||||||
|
db().delete(new_group)
|
||||||
|
raise errors.CannotCreate(exc.message)
|
||||||
|
|
||||||
|
db().flush()
|
||||||
|
return new_group
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupCollection(NailgunCollection):
|
||||||
|
|
||||||
|
single = NodeGroup
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_cluster_id(cls, cluster_id):
|
||||||
|
if cluster_id == '':
|
||||||
|
return cls.filter_by(cluster_id=None)
|
||||||
|
return cls.filter_by(cluster_id=cluster_id)
|
|
@ -14,14 +14,13 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from nailgun import objects
|
from nailgun.network.manager import NetworkManager
|
||||||
|
|
||||||
from nailgun.objects.serializers.base import BasicSerializer
|
from nailgun.objects.serializers.base import BasicSerializer
|
||||||
|
|
||||||
|
|
||||||
class NetworkConfigurationSerializer(BasicSerializer):
|
class NetworkConfigurationSerializer(BasicSerializer):
|
||||||
|
|
||||||
fields = ('id', 'cluster_id', 'name', 'cidr',
|
fields = ('id', 'group_id', 'name', 'cidr',
|
||||||
'gateway', 'vlan_start', 'meta')
|
'gateway', 'vlan_start', 'meta')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -39,21 +38,19 @@ class NetworkConfigurationSerializer(BasicSerializer):
|
||||||
@classmethod
|
@classmethod
|
||||||
def serialize_net_groups_and_vips(cls, cluster):
|
def serialize_net_groups_and_vips(cls, cluster):
|
||||||
result = {}
|
result = {}
|
||||||
net_manager = objects.Cluster.get_network_manager(cluster)
|
net_manager = NetworkManager
|
||||||
|
nets = cluster.network_groups + [net_manager.get_admin_network_group()]
|
||||||
|
|
||||||
result['networks'] = map(
|
result['networks'] = map(
|
||||||
cls.serialize_network_group,
|
cls.serialize_network_group,
|
||||||
cluster.network_groups
|
nets
|
||||||
)
|
|
||||||
result['networks'].append(
|
|
||||||
cls.serialize_network_group(
|
|
||||||
net_manager.get_admin_network_group()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if cluster.is_ha_mode:
|
if cluster.is_ha_mode:
|
||||||
for ng in cluster.network_groups:
|
for ng in cluster.network_groups:
|
||||||
if ng.meta.get("assign_vip"):
|
if ng.meta.get("assign_vip"):
|
||||||
result['{0}_vip'.format(ng.name)] = \
|
result['{0}_vip'.format(ng.name)] = \
|
||||||
net_manager.assign_vip(cluster.id, ng.name)
|
net_manager.assign_vip(cluster.id, ng.name)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -41,7 +41,8 @@ class NodeSerializer(BasicSerializer):
|
||||||
'error_type',
|
'error_type',
|
||||||
'online',
|
'online',
|
||||||
'cluster',
|
'cluster',
|
||||||
'network_data'
|
'network_data',
|
||||||
|
'group_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nailgun.objects.serializers.base import BasicSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroupSerializer(BasicSerializer):
|
||||||
|
|
||||||
|
fields = (
|
||||||
|
"id",
|
||||||
|
"cluster",
|
||||||
|
"name"
|
||||||
|
)
|
|
@ -57,7 +57,7 @@ class NetworkDeploymentSerializer(object):
|
||||||
def get_common_attrs(cls, cluster, attrs):
|
def get_common_attrs(cls, cluster, attrs):
|
||||||
"""Cluster network attributes."""
|
"""Cluster network attributes."""
|
||||||
common = cls.network_provider_cluster_attrs(cluster)
|
common = cls.network_provider_cluster_attrs(cluster)
|
||||||
common.update(cls.network_ranges(cluster))
|
common.update(cls.network_ranges(cluster.default_group))
|
||||||
common.update({'master_ip': settings.MASTER_IP})
|
common.update({'master_ip': settings.MASTER_IP})
|
||||||
common['nodes'] = deepcopy(attrs['nodes'])
|
common['nodes'] = deepcopy(attrs['nodes'])
|
||||||
|
|
||||||
|
@ -93,11 +93,11 @@ class NetworkDeploymentSerializer(object):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def network_ranges(cls, cluster):
|
def network_ranges(cls, group_id):
|
||||||
"""Returns ranges for network groups
|
"""Returns ranges for network groups
|
||||||
except range for public network
|
except range for public network for each node
|
||||||
"""
|
"""
|
||||||
ng_db = db().query(NetworkGroup).filter_by(cluster_id=cluster.id).all()
|
ng_db = db().query(NetworkGroup).filter_by(group_id=group_id).all()
|
||||||
attrs = {}
|
attrs = {}
|
||||||
for net in ng_db:
|
for net in ng_db:
|
||||||
net_name = net.name + '_network_range'
|
net_name = net.name + '_network_range'
|
||||||
|
@ -138,11 +138,13 @@ class NetworkDeploymentSerializer(object):
|
||||||
def get_admin_ip_w_prefix(node):
|
def get_admin_ip_w_prefix(node):
|
||||||
"""Getting admin ip and assign prefix from admin network."""
|
"""Getting admin ip and assign prefix from admin network."""
|
||||||
network_manager = objects.Node.get_network_manager(node)
|
network_manager = objects.Node.get_network_manager(node)
|
||||||
admin_ip = network_manager.get_admin_ip_for_node(node)
|
admin_ip = network_manager.get_admin_ip_for_node(node.id)
|
||||||
admin_ip = IPNetwork(admin_ip)
|
admin_ip = IPNetwork(admin_ip)
|
||||||
|
|
||||||
# Assign prefix from admin network
|
# Assign prefix from admin network
|
||||||
admin_net = IPNetwork(network_manager.get_admin_network_group().cidr)
|
admin_net = IPNetwork(
|
||||||
|
network_manager.get_admin_network_group(node.id).cidr
|
||||||
|
)
|
||||||
admin_ip.prefixlen = admin_net.prefixlen
|
admin_ip.prefixlen = admin_net.prefixlen
|
||||||
|
|
||||||
return str(admin_ip)
|
return str(admin_ip)
|
||||||
|
@ -231,11 +233,12 @@ class NovaNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
||||||
if network.get('ip'):
|
if network.get('ip'):
|
||||||
interface['ipaddr'].append(network.get('ip'))
|
interface['ipaddr'].append(network.get('ip'))
|
||||||
|
|
||||||
if network_name == 'admin':
|
if network_name == 'fuelweb_admin':
|
||||||
admin_ip_addr = cls.get_admin_ip_w_prefix(node)
|
admin_ip_addr = cls.get_admin_ip_w_prefix(node)
|
||||||
interface['ipaddr'].append(admin_ip_addr)
|
interface['ipaddr'].append(admin_ip_addr)
|
||||||
elif network_name == 'public' and network.get('gateway'):
|
elif network_name == 'public' and network.get('gateway'):
|
||||||
interface['gateway'] = network['gateway']
|
interface['gateway'] = network['gateway']
|
||||||
|
interface['default_gateway'] = True
|
||||||
|
|
||||||
for if_name, if_data in interfaces.iteritems():
|
for if_name, if_data in interfaces.iteritems():
|
||||||
if len(if_data['ipaddr']) == 0:
|
if len(if_data['ipaddr']) == 0:
|
||||||
|
@ -513,9 +516,9 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
||||||
# We have to add them after br-ethXX bridges because it is the way
|
# We have to add them after br-ethXX bridges because it is the way
|
||||||
# to provide a right ordering of ifdown/ifup operations with
|
# to provide a right ordering of ifdown/ifup operations with
|
||||||
# IP interfaces.
|
# IP interfaces.
|
||||||
brnames = ['br-mgmt', 'br-storage', 'br-fw-admin']
|
brnames = ['br-ex', 'br-mgmt', 'br-storage', 'br-fw-admin']
|
||||||
if objects.Node.should_have_public(node):
|
if not objects.Node.should_have_public(node):
|
||||||
brnames.append('br-ex')
|
brnames.pop(0)
|
||||||
|
|
||||||
for brname in brnames:
|
for brname in brnames:
|
||||||
attrs['transformations'].append({
|
attrs['transformations'].append({
|
||||||
|
@ -532,18 +535,37 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
||||||
if objects.Node.should_have_public(node):
|
if objects.Node.should_have_public(node):
|
||||||
netgroup_mapping.append(('public', 'br-ex'))
|
netgroup_mapping.append(('public', 'br-ex'))
|
||||||
|
|
||||||
|
# Include information about all subnets that don't belong to this node.
|
||||||
|
# This is used during deployment to configure routes to all other
|
||||||
|
# networks in the environment.
|
||||||
|
other_nets = nm.get_networks_not_on_node(node)
|
||||||
|
|
||||||
netgroups = {}
|
netgroups = {}
|
||||||
for ngname, brname in netgroup_mapping:
|
for ngname, brname in netgroup_mapping:
|
||||||
# Here we get a dict with network description for this particular
|
# Here we get a dict with network description for this particular
|
||||||
# node with its assigned IPs and device names for each network.
|
# node with its assigned IPs and device names for each network.
|
||||||
netgroup = nm.get_node_network_by_netname(node, ngname)
|
netgroup = nm.get_node_network_by_netname(node, ngname)
|
||||||
attrs['endpoints'][brname]['IP'] = [netgroup['ip']]
|
if netgroup.get('ip'):
|
||||||
|
attrs['endpoints'][brname]['IP'] = [netgroup['ip']]
|
||||||
|
if netgroup.get('gateway'):
|
||||||
|
attrs['endpoints'][brname]['gateway'] = netgroup['gateway']
|
||||||
|
|
||||||
|
attrs['endpoints'][brname]['other_nets'] = \
|
||||||
|
other_nets.get(ngname, [])
|
||||||
|
|
||||||
netgroups[ngname] = netgroup
|
netgroups[ngname] = netgroup
|
||||||
|
|
||||||
if objects.Node.should_have_public(node):
|
if objects.Node.should_have_public(node):
|
||||||
attrs['endpoints']['br-ex']['gateway'] = \
|
attrs['endpoints']['br-ex']['gateway'] = \
|
||||||
netgroups['public']['gateway']
|
netgroups['public']['gateway']
|
||||||
else:
|
else:
|
||||||
attrs['endpoints']['br-fw-admin']['gateway'] = settings.MASTER_IP
|
gw = nm.get_default_gateway(node.id)
|
||||||
|
attrs['endpoints']['br-fw-admin']['gateway'] = gw
|
||||||
|
|
||||||
|
for brname in brnames:
|
||||||
|
if attrs['endpoints'][brname].get('gateway'):
|
||||||
|
attrs['endpoints'][brname]['default_gateway'] = True
|
||||||
|
break
|
||||||
|
|
||||||
# Connect interface bridges to network bridges.
|
# Connect interface bridges to network bridges.
|
||||||
for ngname, brname in netgroup_mapping:
|
for ngname, brname in netgroup_mapping:
|
||||||
|
@ -627,7 +649,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
|
||||||
NetworkGroup.cidr,
|
NetworkGroup.cidr,
|
||||||
NetworkGroup.gateway
|
NetworkGroup.gateway
|
||||||
).filter_by(
|
).filter_by(
|
||||||
cluster_id=cluster.id,
|
group_id=cluster.default_group,
|
||||||
name='public'
|
name='public'
|
||||||
).first()
|
).first()
|
||||||
join_range = lambda r: (":".join(map(str, r)) if r else None)
|
join_range = lambda r: (":".join(map(str, r)) if r else None)
|
||||||
|
@ -945,6 +967,10 @@ class DeploymentMultinodeSerializer(object):
|
||||||
|
|
||||||
node_attrs.update(self.get_net_provider_serializer(
|
node_attrs.update(self.get_net_provider_serializer(
|
||||||
node.cluster).get_node_attrs(node))
|
node.cluster).get_node_attrs(node))
|
||||||
|
node_attrs.update(
|
||||||
|
self.get_net_provider_serializer(node.cluster).
|
||||||
|
network_ranges(node.group_id)
|
||||||
|
)
|
||||||
node_attrs.update(self.get_image_cache_max_size(node))
|
node_attrs.update(self.get_image_cache_max_size(node))
|
||||||
node_attrs.update(self.generate_test_vm_image_data(node))
|
node_attrs.update(self.generate_test_vm_image_data(node))
|
||||||
return node_attrs
|
return node_attrs
|
||||||
|
|
|
@ -147,6 +147,13 @@ class ProvisioningSerializer(object):
|
||||||
'mlnx_iser_enabled': cluster_attrs['storage']['iser'],
|
'mlnx_iser_enabled': cluster_attrs['storage']['iser'],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
net_manager = objects.Node.get_network_manager(node)
|
||||||
|
gw = net_manager.get_default_gateway(node.id)
|
||||||
|
serialized_node['ks_meta'].update({'gw': gw})
|
||||||
|
serialized_node['ks_meta'].update(
|
||||||
|
{'admin_net': net_manager.get_admin_network_group(node.id).cidr}
|
||||||
|
)
|
||||||
|
|
||||||
serialized_node.update(cls.serialize_interfaces(node))
|
serialized_node.update(cls.serialize_interfaces(node))
|
||||||
|
|
||||||
return serialized_node
|
return serialized_node
|
||||||
|
@ -156,9 +163,9 @@ class ProvisioningSerializer(object):
|
||||||
interfaces = {}
|
interfaces = {}
|
||||||
interfaces_extra = {}
|
interfaces_extra = {}
|
||||||
net_manager = objects.Node.get_network_manager(node)
|
net_manager = objects.Node.get_network_manager(node)
|
||||||
admin_ip = net_manager.get_admin_ip_for_node(node)
|
admin_ip = net_manager.get_admin_ip_for_node(node.id)
|
||||||
admin_netmask = str(netaddr.IPNetwork(
|
admin_netmask = str(netaddr.IPNetwork(
|
||||||
net_manager.get_admin_network_group().cidr
|
net_manager.get_admin_network_group(node.id).cidr
|
||||||
).netmask)
|
).netmask)
|
||||||
|
|
||||||
for interface in node.nic_interfaces:
|
for interface in node.nic_interfaces:
|
||||||
|
|
|
@ -702,6 +702,16 @@ class VerifyNetworksTaskManager(TaskManager):
|
||||||
db().commit()
|
db().commit()
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
if len(self.cluster.node_groups) > 1:
|
||||||
|
task.status = TASK_STATUSES.error
|
||||||
|
task.progress = 100
|
||||||
|
task.message = ('Network verfiication is disabled for '
|
||||||
|
'environments containing more than one node '
|
||||||
|
'group.')
|
||||||
|
db().add(task)
|
||||||
|
db().commit()
|
||||||
|
return task
|
||||||
|
|
||||||
if self.cluster.status in self._blocking_statuses:
|
if self.cluster.status in self._blocking_statuses:
|
||||||
task.status = TASK_STATUSES.error
|
task.status = TASK_STATUSES.error
|
||||||
task.progress = 100
|
task.progress = 100
|
||||||
|
|
|
@ -212,7 +212,7 @@ class ProvisionTask(object):
|
||||||
|
|
||||||
admin_net_id = objects.Node.get_network_manager(
|
admin_net_id = objects.Node.get_network_manager(
|
||||||
node
|
node
|
||||||
).get_admin_network_group_id()
|
).get_admin_network_group_id(node.id)
|
||||||
|
|
||||||
TaskHelper.prepare_syslog_dir(node, admin_net_id)
|
TaskHelper.prepare_syslog_dir(node, admin_net_id)
|
||||||
db().commit()
|
db().commit()
|
||||||
|
@ -364,7 +364,7 @@ class StopDeploymentTask(object):
|
||||||
'slave_name': objects.Node.make_slave_name(n),
|
'slave_name': objects.Node.make_slave_name(n),
|
||||||
'admin_ip': objects.Node.get_network_manager(
|
'admin_ip': objects.Node.get_network_manager(
|
||||||
n
|
n
|
||||||
).get_admin_ip_for_node(n)
|
).get_admin_ip_for_node(n.id)
|
||||||
} for n in nodes_to_stop
|
} for n in nodes_to_stop
|
||||||
],
|
],
|
||||||
"engine": {
|
"engine": {
|
||||||
|
@ -453,7 +453,7 @@ class BaseNetworkVerification(object):
|
||||||
vlans = []
|
vlans = []
|
||||||
for ng in assigned_networks:
|
for ng in assigned_networks:
|
||||||
# Handle FuelWeb admin network first.
|
# Handle FuelWeb admin network first.
|
||||||
if not ng.cluster_id:
|
if ng.group_id is None:
|
||||||
vlans.append(0)
|
vlans.append(0)
|
||||||
continue
|
continue
|
||||||
data_ng = filter(lambda i: i['name'] == ng.name,
|
data_ng = filter(lambda i: i['name'] == ng.name,
|
||||||
|
|
|
@ -183,6 +183,7 @@ class Environment(object):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cluster = Cluster.create(cluster_data)
|
cluster = Cluster.create(cluster_data)
|
||||||
|
cluster.create_default_group()
|
||||||
db().commit()
|
db().commit()
|
||||||
self.clusters.append(cluster)
|
self.clusters.append(cluster)
|
||||||
|
|
||||||
|
@ -211,6 +212,7 @@ class Environment(object):
|
||||||
node_data = {
|
node_data = {
|
||||||
'mac': mac,
|
'mac': mac,
|
||||||
'status': 'discover',
|
'status': 'discover',
|
||||||
|
'ip': '10.20.0.130',
|
||||||
'meta': default_metadata
|
'meta': default_metadata
|
||||||
}
|
}
|
||||||
if kwargs:
|
if kwargs:
|
||||||
|
|
|
@ -80,7 +80,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
|
|
||||||
'management_interface': 'eth0.101',
|
'management_interface': 'eth0.101',
|
||||||
'fixed_interface': 'eth0.103',
|
'fixed_interface': 'eth0.103',
|
||||||
'admin_interface': 'eth1',
|
'fuelweb_admin_interface': 'eth1',
|
||||||
'storage_interface': 'eth0.102',
|
'storage_interface': 'eth0.102',
|
||||||
'public_interface': 'eth0',
|
'public_interface': 'eth0',
|
||||||
'floating_interface': 'eth0',
|
'floating_interface': 'eth0',
|
||||||
|
@ -202,7 +202,8 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
'eth0': {
|
'eth0': {
|
||||||
'interface': 'eth0',
|
'interface': 'eth0',
|
||||||
'ipaddr': ['%s/24' % ips['public']],
|
'ipaddr': ['%s/24' % ips['public']],
|
||||||
'gateway': '172.16.0.1'},
|
'gateway': '172.16.0.1',
|
||||||
|
'default_gateway': True},
|
||||||
'eth0.101': {
|
'eth0.101': {
|
||||||
'interface': 'eth0.101',
|
'interface': 'eth0.101',
|
||||||
'ipaddr': ['%s/24' % ips['internal']]},
|
'ipaddr': ['%s/24' % ips['internal']]},
|
||||||
|
@ -296,6 +297,10 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
'mlnx_vf_num': "16",
|
'mlnx_vf_num': "16",
|
||||||
'mlnx_plugin_mode': "disabled",
|
'mlnx_plugin_mode': "disabled",
|
||||||
'mlnx_iser_enabled': False,
|
'mlnx_iser_enabled': False,
|
||||||
|
'gw':
|
||||||
|
self.env.network_manager.get_default_gateway(n.id),
|
||||||
|
'admin_net':
|
||||||
|
self.env.network_manager.get_admin_network_group(n.id).cidr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
orchestrator_data = objects.Release.get_orchestrator_data_dict(
|
orchestrator_data = objects.Release.get_orchestrator_data_dict(
|
||||||
|
@ -310,7 +315,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
|
|
||||||
NetworkManager.assign_admin_ips([n])
|
NetworkManager.assign_admin_ips([n])
|
||||||
|
|
||||||
admin_ip = self.env.network_manager.get_admin_ip_for_node(n)
|
admin_ip = self.env.network_manager.get_admin_ip_for_node(n.id)
|
||||||
|
|
||||||
for i in n.interfaces:
|
for i in n.interfaces:
|
||||||
if 'interfaces' not in pnd:
|
if 'interfaces' not in pnd:
|
||||||
|
@ -609,7 +614,8 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
"br-mgmt": {"IP": [ips['management'] + "/24"]},
|
"br-mgmt": {"IP": [ips['management'] + "/24"]},
|
||||||
"br-ex": {
|
"br-ex": {
|
||||||
"IP": [ips['public'] + "/24"],
|
"IP": [ips['public'] + "/24"],
|
||||||
"gateway": "172.16.0.1"
|
"default_gateway": True,
|
||||||
|
"gateway": "172.16.0.1",
|
||||||
},
|
},
|
||||||
"br-storage": {"IP": [ips['storage'] + "/24"]},
|
"br-storage": {"IP": [ips['storage'] + "/24"]},
|
||||||
"br-fw-admin": {"IP": [ips['admin']]},
|
"br-fw-admin": {"IP": [ips['admin']]},
|
||||||
|
@ -636,6 +642,9 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
"action": "add-port",
|
"action": "add-port",
|
||||||
"bridge": u"br-eth1",
|
"bridge": u"br-eth1",
|
||||||
"name": u"eth1"},
|
"name": u"eth1"},
|
||||||
|
{
|
||||||
|
"action": "add-br",
|
||||||
|
"name": "br-ex"},
|
||||||
{
|
{
|
||||||
"action": "add-br",
|
"action": "add-br",
|
||||||
"name": "br-mgmt"},
|
"name": "br-mgmt"},
|
||||||
|
@ -645,9 +654,6 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
{
|
{
|
||||||
"action": "add-br",
|
"action": "add-br",
|
||||||
"name": "br-fw-admin"},
|
"name": "br-fw-admin"},
|
||||||
{
|
|
||||||
"action": "add-br",
|
|
||||||
"name": "br-ex"},
|
|
||||||
{
|
{
|
||||||
"action": "add-patch",
|
"action": "add-patch",
|
||||||
"bridges": [u"br-eth0", "br-storage"],
|
"bridges": [u"br-eth0", "br-storage"],
|
||||||
|
@ -746,7 +752,11 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
'mlnx_vf_num': "16",
|
'mlnx_vf_num': "16",
|
||||||
'mlnx_plugin_mode': "disabled",
|
'mlnx_plugin_mode': "disabled",
|
||||||
'mlnx_iser_enabled': False,
|
'mlnx_iser_enabled': False,
|
||||||
'image_data': cluster_attrs['provision']['image_data']
|
'image_data': cluster_attrs['provision']['image_data'],
|
||||||
|
'gw':
|
||||||
|
self.env.network_manager.get_default_gateway(n.id),
|
||||||
|
'admin_net':
|
||||||
|
self.env.network_manager.get_admin_network_group(n.id).cidr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
orchestrator_data = objects.Release.get_orchestrator_data_dict(
|
orchestrator_data = objects.Release.get_orchestrator_data_dict(
|
||||||
|
@ -761,7 +771,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
|
|
||||||
NetworkManager.assign_admin_ips([n])
|
NetworkManager.assign_admin_ips([n])
|
||||||
|
|
||||||
admin_ip = self.env.network_manager.get_admin_ip_for_node(n)
|
admin_ip = self.env.network_manager.get_admin_ip_for_node(n.id)
|
||||||
|
|
||||||
for i in n.meta.get('interfaces', []):
|
for i in n.meta.get('interfaces', []):
|
||||||
if 'interfaces' not in pnd:
|
if 'interfaces' not in pnd:
|
||||||
|
|
|
@ -70,7 +70,7 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
for clstr in (cluster_db, cluster2_db):
|
for clstr in (cluster_db, cluster2_db):
|
||||||
management_net = self.db.query(NetworkGroup).filter_by(
|
management_net = self.db.query(NetworkGroup).filter_by(
|
||||||
name="management",
|
name="management",
|
||||||
cluster_id=clstr.id
|
group_id=clstr.default_group
|
||||||
).first()
|
).first()
|
||||||
NovaNetworkManager.update(
|
NovaNetworkManager.update(
|
||||||
clstr,
|
clstr,
|
||||||
|
@ -95,11 +95,14 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
cluster2_nets = self._get_cluster_networks(cluster2["id"])
|
cluster2_nets = self._get_cluster_networks(cluster2["id"])
|
||||||
|
|
||||||
for net1, net2 in zip(cluster1_nets, cluster2_nets):
|
for net1, net2 in zip(cluster1_nets, cluster2_nets):
|
||||||
for f in ('cluster_id', 'id'):
|
for f in ('group_id', 'id'):
|
||||||
del net1[f]
|
del net1[f]
|
||||||
del net2[f]
|
del net2[f]
|
||||||
|
|
||||||
self.assertEqual(cluster1_nets, cluster2_nets)
|
cluster1_nets = sorted(cluster1_nets, key=lambda n: n['name'])
|
||||||
|
cluster2_nets = sorted(cluster2_nets, key=lambda n: n['name'])
|
||||||
|
|
||||||
|
self.assertEquals(cluster1_nets, cluster2_nets)
|
||||||
|
|
||||||
def test_cluster_creation_same_networks(self):
|
def test_cluster_creation_same_networks(self):
|
||||||
cluster1_id = self.env.create_cluster(api=True)["id"]
|
cluster1_id = self.env.create_cluster(api=True)["id"]
|
||||||
|
@ -108,12 +111,12 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
cluster2_nets = self._get_cluster_networks(cluster2_id)
|
cluster2_nets = self._get_cluster_networks(cluster2_id)
|
||||||
|
|
||||||
for net1, net2 in zip(cluster1_nets, cluster2_nets):
|
for net1, net2 in zip(cluster1_nets, cluster2_nets):
|
||||||
for f in ('cluster_id', 'id'):
|
for f in ('group_id', 'id'):
|
||||||
del net1[f]
|
del net1[f]
|
||||||
del net2[f]
|
del net2[f]
|
||||||
|
|
||||||
cluster1_nets = sorted(cluster1_nets, key=lambda n: n['vlan_start'])
|
cluster1_nets = sorted(cluster1_nets, key=lambda n: n['name'])
|
||||||
cluster2_nets = sorted(cluster2_nets, key=lambda n: n['vlan_start'])
|
cluster2_nets = sorted(cluster2_nets, key=lambda n: n['name'])
|
||||||
|
|
||||||
self.assertEqual(cluster1_nets, cluster2_nets)
|
self.assertEqual(cluster1_nets, cluster2_nets)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ class TestHorizonURL(BaseIntegrationTest):
|
||||||
self.env.wait_ready(supertask, 60)
|
self.env.wait_ready(supertask, 60)
|
||||||
|
|
||||||
network = self.db.query(NetworkGroup).\
|
network = self.db.query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == self.env.clusters[0].id).\
|
filter(NetworkGroup.group_id ==
|
||||||
|
self.env.clusters[0].default_group).\
|
||||||
filter_by(name="public").first()
|
filter_by(name="public").first()
|
||||||
lost_ips = self.db.query(IPAddr).filter_by(
|
lost_ips = self.db.query(IPAddr).filter_by(
|
||||||
network=network.id,
|
network=network.id,
|
||||||
|
|
|
@ -57,8 +57,7 @@ class TestMellanox(OrchestratorSerializerTestBase):
|
||||||
iser_sttr = editable_attrs.setdefault('storage', {})
|
iser_sttr = editable_attrs.setdefault('storage', {})
|
||||||
iser_sttr.setdefault('iser', {})['value'] = True
|
iser_sttr.setdefault('iser', {})['value'] = True
|
||||||
network_group = self.db().query(NetworkGroup)
|
network_group = self.db().query(NetworkGroup)
|
||||||
storage = network_group.filter_by(name="storage",
|
storage = network_group.filter_by(name="storage")
|
||||||
cluster_id=self.cluster_id)
|
|
||||||
if iser_vlan:
|
if iser_vlan:
|
||||||
storage.update(
|
storage.update(
|
||||||
{"vlan_start": iser_vlan}, synchronize_session="fetch")
|
{"vlan_start": iser_vlan}, synchronize_session="fetch")
|
||||||
|
|
|
@ -45,7 +45,7 @@ class TestNovaNetworkConfigurationHandlerMultinode(BaseIntegrationTest):
|
||||||
|
|
||||||
keys = [
|
keys = [
|
||||||
'name',
|
'name',
|
||||||
'cluster_id',
|
'group_id',
|
||||||
'vlan_start',
|
'vlan_start',
|
||||||
'cidr',
|
'cidr',
|
||||||
'id']
|
'id']
|
||||||
|
@ -268,7 +268,7 @@ class TestNeutronNetworkConfigurationHandlerMultinode(BaseIntegrationTest):
|
||||||
|
|
||||||
keys = [
|
keys = [
|
||||||
'name',
|
'name',
|
||||||
'cluster_id',
|
'group_id',
|
||||||
'vlan_start',
|
'vlan_start',
|
||||||
'cidr',
|
'cidr',
|
||||||
'id']
|
'id']
|
||||||
|
|
|
@ -58,7 +58,7 @@ class TestNetworkManager(BaseIntegrationTest):
|
||||||
|
|
||||||
management_net = self.db.query(NetworkGroup).\
|
management_net = self.db.query(NetworkGroup).\
|
||||||
filter(
|
filter(
|
||||||
NetworkGroup.cluster_id == self.env.clusters[0].id
|
NetworkGroup.group_id == self.env.clusters[0].default_group
|
||||||
).filter_by(
|
).filter_by(
|
||||||
name='management'
|
name='management'
|
||||||
).first()
|
).first()
|
||||||
|
|
|
@ -334,6 +334,14 @@ class TestHandlers(BaseIntegrationTest):
|
||||||
|
|
||||||
# Set IP outside of admin network range on eth1
|
# Set IP outside of admin network range on eth1
|
||||||
node.meta['interfaces'][1]['ip'] = '10.21.0.3'
|
node.meta['interfaces'][1]['ip'] = '10.21.0.3'
|
||||||
|
self.app.put(
|
||||||
|
reverse('NodeAgentHandler'),
|
||||||
|
jsonutils.dumps({
|
||||||
|
'mac': node.mac,
|
||||||
|
'meta': node.meta,
|
||||||
|
}),
|
||||||
|
headers=self.default_headers)
|
||||||
|
|
||||||
self.env.network_manager.update_interfaces_info(node)
|
self.env.network_manager.update_interfaces_info(node)
|
||||||
|
|
||||||
# node.mac == eth0 mac so eth0 should now be admin interface
|
# node.mac == eth0 mac so eth0 should now be admin interface
|
||||||
|
|
|
@ -293,7 +293,8 @@ class TestNovaOrchestratorSerializer(OrchestratorSerializerTestBase):
|
||||||
'ipaddr': ['172.16.0.2/24',
|
'ipaddr': ['172.16.0.2/24',
|
||||||
'192.168.0.1/24',
|
'192.168.0.1/24',
|
||||||
'192.168.1.1/24'],
|
'192.168.1.1/24'],
|
||||||
'gateway': '172.16.0.1'
|
'gateway': '172.16.0.1',
|
||||||
|
'default_gateway': True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.datadiff(expected_interfaces, interfaces, ignore_keys=['ipaddr'])
|
self.datadiff(expected_interfaces, interfaces, ignore_keys=['ipaddr'])
|
||||||
|
@ -977,7 +978,7 @@ class TestNeutronOrchestratorSerializer(OrchestratorSerializerTestBase):
|
||||||
public_ng = self.db.query(NetworkGroup).filter(
|
public_ng = self.db.query(NetworkGroup).filter(
|
||||||
NetworkGroup.name == 'public'
|
NetworkGroup.name == 'public'
|
||||||
).filter(
|
).filter(
|
||||||
NetworkGroup.cluster_id == cluster.id
|
NetworkGroup.group_id == cluster.default_group
|
||||||
).first()
|
).first()
|
||||||
public_ng.gateway = test_gateway
|
public_ng.gateway = test_gateway
|
||||||
self.db.add(public_ng)
|
self.db.add(public_ng)
|
||||||
|
|
|
@ -67,6 +67,7 @@ class TestProvisioning(BaseIntegrationTest):
|
||||||
)
|
)
|
||||||
cluster = self.env.clusters[0]
|
cluster = self.env.clusters[0]
|
||||||
objects.Cluster.clear_pending_changes(cluster)
|
objects.Cluster.clear_pending_changes(cluster)
|
||||||
|
self.env.network_manager.assign_ips(self.env.nodes, 'fuelweb_admin')
|
||||||
self.env.network_manager.assign_ips(self.env.nodes, 'management')
|
self.env.network_manager.assign_ips(self.env.nodes, 'management')
|
||||||
self.env.network_manager.assign_ips(self.env.nodes, 'storage')
|
self.env.network_manager.assign_ips(self.env.nodes, 'storage')
|
||||||
self.env.network_manager.assign_ips(self.env.nodes, 'public')
|
self.env.network_manager.assign_ips(self.env.nodes, 'public')
|
||||||
|
|
|
@ -1148,7 +1148,8 @@ class TestConsumer(BaseIntegrationTest):
|
||||||
cluster_id=cluster_id
|
cluster_id=cluster_id
|
||||||
)
|
)
|
||||||
networks = self.db.query(NetworkGroup).\
|
networks = self.db.query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster_id).all()
|
filter(NetworkGroup.group_id ==
|
||||||
|
self.env.clusters[0].default_group).all()
|
||||||
|
|
||||||
vlans = []
|
vlans = []
|
||||||
for net in networks:
|
for net in networks:
|
||||||
|
@ -1190,8 +1191,9 @@ class TestConsumer(BaseIntegrationTest):
|
||||||
self.assertEqual(len(nots_db), 0)
|
self.assertEqual(len(nots_db), 0)
|
||||||
|
|
||||||
nets_db = self.db.query(NetworkGroup).\
|
nets_db = self.db.query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster_id).all()
|
filter(NetworkGroup.group_id ==
|
||||||
self.assertEqual(len(nets_db), 0)
|
self.env.clusters[0].default_group).all()
|
||||||
|
self.assertEquals(len(nets_db), 0)
|
||||||
|
|
||||||
task_db = self.db.query(Task)\
|
task_db = self.db.query(Task)\
|
||||||
.filter_by(cluster_id=cluster_id).all()
|
.filter_by(cluster_id=cluster_id).all()
|
||||||
|
@ -1247,5 +1249,6 @@ class TestConsumer(BaseIntegrationTest):
|
||||||
self.assertNotEqual(len(nots_db), 0)
|
self.assertNotEqual(len(nots_db), 0)
|
||||||
|
|
||||||
nets_db = self.db.query(NetworkGroup).\
|
nets_db = self.db.query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster_db.id).all()
|
filter(NetworkGroup.group_id ==
|
||||||
|
self.env.clusters[0].default_group).all()
|
||||||
self.assertNotEqual(len(nets_db), 0)
|
self.assertNotEqual(len(nets_db), 0)
|
||||||
|
|
|
@ -80,7 +80,7 @@ class TestStopDeployment(BaseIntegrationTest):
|
||||||
n["admin_ip"],
|
n["admin_ip"],
|
||||||
objects.Node.get_network_manager(
|
objects.Node.get_network_manager(
|
||||||
n_db
|
n_db
|
||||||
).get_admin_ip_for_node(n_db)
|
).get_admin_ip_for_node(n_db.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@fake_tasks(recover_nodes=False, tick_interval=1)
|
@fake_tasks(recover_nodes=False, tick_interval=1)
|
||||||
|
|
|
@ -116,7 +116,8 @@ class TestLogs(BaseIntegrationTest):
|
||||||
'text2',
|
'text2',
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
cluster = self.env.create_cluster(api=False)
|
self.env.create_cluster()
|
||||||
|
cluster = self.env.clusters[0]
|
||||||
node = self.env.create_node(cluster_id=cluster.id, ip=node_ip)
|
node = self.env.create_node(cluster_id=cluster.id, ip=node_ip)
|
||||||
self._create_logfile_for_node(settings.LOGS[0], log_entries)
|
self._create_logfile_for_node(settings.LOGS[0], log_entries)
|
||||||
self._create_logfile_for_node(settings.LOGS[1], log_entries, node)
|
self._create_logfile_for_node(settings.LOGS[1], log_entries, node)
|
||||||
|
|
|
@ -26,7 +26,10 @@ class TestAssignmentHandlers(BaseIntegrationTest):
|
||||||
self.env.create(
|
self.env.create(
|
||||||
cluster_kwargs={"api": True},
|
cluster_kwargs={"api": True},
|
||||||
nodes_kwargs=[
|
nodes_kwargs=[
|
||||||
{"cluster_id": None}
|
{
|
||||||
|
"cluster_id": None,
|
||||||
|
"api": True
|
||||||
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
cluster = self.env.clusters[0]
|
cluster = self.env.clusters[0]
|
||||||
|
|
|
@ -58,7 +58,7 @@ class TestNodeDeletion(BaseIntegrationTest):
|
||||||
self.assertEqual(node_try, None)
|
self.assertEqual(node_try, None)
|
||||||
|
|
||||||
management_net = self.db.query(NetworkGroup).\
|
management_net = self.db.query(NetworkGroup).\
|
||||||
filter(NetworkGroup.cluster_id == cluster.id).filter_by(
|
filter(NetworkGroup.group_id == cluster.default_group).filter_by(
|
||||||
name='management').first()
|
name='management').first()
|
||||||
|
|
||||||
ipaddrs = self.db.query(IPAddr).\
|
ipaddrs = self.db.query(IPAddr).\
|
||||||
|
|
|
@ -373,7 +373,7 @@ class TestNodeDefaultsDisksHandler(BaseIntegrationTest):
|
||||||
self.assertEqual(len(disk['volumes']), len(vgs))
|
self.assertEqual(len(disk['volumes']), len(vgs))
|
||||||
|
|
||||||
def test_get_default_attrs(self):
|
def test_get_default_attrs(self):
|
||||||
self.env.create_node(api=True)
|
self.env.create_node(api=False)
|
||||||
node_db = self.env.nodes[0]
|
node_db = self.env.nodes[0]
|
||||||
volumes_from_api = self.get(node_db.id)
|
volumes_from_api = self.get(node_db.id)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2014 Mirantis, 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 express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from nailgun.db import db
|
||||||
|
from nailgun.db.sqlalchemy.models import NetworkGroup
|
||||||
|
from nailgun.test.base import BaseIntegrationTest
|
||||||
|
from nailgun.test.base import reverse
|
||||||
|
|
||||||
|
|
||||||
|
class TestNodeGroups(BaseIntegrationTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNodeGroups, self).setUp()
|
||||||
|
self.cluster = self.env.create_cluster(
|
||||||
|
api=False,
|
||||||
|
net_provider='neutron',
|
||||||
|
net_segment_type='gre'
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_node_group(self):
|
||||||
|
resp = self.app.post(
|
||||||
|
reverse('NodeGroupCollectionHandler'),
|
||||||
|
json.dumps({'cluster_id': self.cluster['id'], 'name': 'test_ng'}),
|
||||||
|
headers=self.default_headers,
|
||||||
|
expect_errors=False
|
||||||
|
)
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def test_nodegroup_creation(self):
|
||||||
|
resp = self.create_node_group()
|
||||||
|
|
||||||
|
self.assertEquals(resp.status_code, 201)
|
||||||
|
response = json.loads(resp.body)
|
||||||
|
self.assertEquals(response['cluster'], self.cluster['id'])
|
||||||
|
|
||||||
|
def test_nodegroup_assignment(self):
|
||||||
|
self.env.create(
|
||||||
|
cluster_kwargs={
|
||||||
|
'api': True,
|
||||||
|
'net_provider': 'neutron',
|
||||||
|
'net_segment_type': 'gre'
|
||||||
|
},
|
||||||
|
nodes_kwargs=[{
|
||||||
|
'roles': [],
|
||||||
|
'pending_roles': ['controller'],
|
||||||
|
'pending_addition': True,
|
||||||
|
'api': True}]
|
||||||
|
)
|
||||||
|
cluster = self.env.clusters[0]
|
||||||
|
node = self.env.nodes[0]
|
||||||
|
|
||||||
|
resp = self.app.post(
|
||||||
|
reverse('NodeGroupCollectionHandler'),
|
||||||
|
json.dumps({'cluster_id': cluster['id'], 'name': 'test_ng'}),
|
||||||
|
headers=self.default_headers,
|
||||||
|
expect_errors=False
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json.loads(resp.body)
|
||||||
|
ng_id = response['id']
|
||||||
|
|
||||||
|
resp = self.app.put(
|
||||||
|
reverse('NodeHandler', kwargs={'obj_id': node['id']}),
|
||||||
|
json.dumps({'group_id': ng_id}),
|
||||||
|
headers=self.default_headers,
|
||||||
|
expect_errors=False
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json.loads(resp.body)
|
||||||
|
self.assertEquals(resp.status_code, 200)
|
||||||
|
self.assertEquals(node.group_id, ng_id)
|
||||||
|
|
||||||
|
def test_nodegroup_create_network(self):
|
||||||
|
resp = self.create_node_group()
|
||||||
|
|
||||||
|
response = json.loads(resp.body)
|
||||||
|
nets = db().query(NetworkGroup).filter_by(group_id=response['id'])
|
||||||
|
self.assertEquals(nets.count(), 4)
|
||||||
|
|
||||||
|
def test_nodegroup_deletion(self):
|
||||||
|
resp = self.create_node_group()
|
||||||
|
response = json.loads(resp.body)
|
||||||
|
group_id = response['id']
|
||||||
|
|
||||||
|
self.app.delete(
|
||||||
|
reverse(
|
||||||
|
'NodeGroupHandler',
|
||||||
|
kwargs={'obj_id': group_id}
|
||||||
|
),
|
||||||
|
headers=self.default_headers,
|
||||||
|
expect_errors=False
|
||||||
|
)
|
||||||
|
|
||||||
|
nets = db().query(NetworkGroup).filter_by(group_id=response['id'])
|
||||||
|
self.assertEquals(nets.count(), 0)
|
||||||
|
|
||||||
|
def test_nodegroup_invalid_segmentation_type(self):
|
||||||
|
cluster = self.env.create_cluster(
|
||||||
|
api=False,
|
||||||
|
net_provider='neutron',
|
||||||
|
net_segment_type='vlan'
|
||||||
|
)
|
||||||
|
resp = self.app.post(
|
||||||
|
reverse('NodeGroupCollectionHandler'),
|
||||||
|
json.dumps({'cluster_id': cluster['id'], 'name': 'test_ng'}),
|
||||||
|
headers=self.default_headers,
|
||||||
|
expect_errors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(resp.status_code, 403)
|
|
@ -271,6 +271,7 @@ class TestNodeObject(BaseIntegrationTest):
|
||||||
self.assertEqual(node_db.pending_roles, [])
|
self.assertEqual(node_db.pending_roles, [])
|
||||||
|
|
||||||
exclude_fields = [
|
exclude_fields = [
|
||||||
|
"group_id",
|
||||||
"id",
|
"id",
|
||||||
"mac",
|
"mac",
|
||||||
"meta",
|
"meta",
|
||||||
|
|
Loading…
Reference in New Issue