Allow multiple networks to be created per cluster

Added a NodeGroup model. NodeGroups can be created and modified
via the Fuel API. Clusters are created with a default node group
and the cluster networks are tied to that default node group.

Upon creation of a node group a set of networks will be generated.
When a node group is deleted those networks will be deleted as well.

See previous change: Ic30a1b46112599022645b06f7bef2223ff4c6475

DocImpact
Change-Id: I8bbe6b8c6338d6586c7e9a61542939ae2a19828b
Implements: blueprint multiple-cluster-networks
This commit is contained in:
Ryan Moe
2014-01-24 14:43:49 -08:00
parent 90a96c33b2
commit 4b1f8a5ec6
5 changed files with 206 additions and 3 deletions

View File

@@ -25,6 +25,7 @@ from fuelclient.cli.actions.interrupt import ResetAction
from fuelclient.cli.actions.interrupt import StopAction
from fuelclient.cli.actions.network import NetworkAction
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.role import RoleAction
from fuelclient.cli.actions.settings import SettingsAction
@@ -49,7 +50,8 @@ actions_tuple = (
SnapshotAction,
HealthCheckAction,
UserAction,
PluginAction
PluginAction,
NodeGroupAction
)
actions = dict(

View File

@@ -33,7 +33,7 @@ class NodeAction(Action):
"""
action_name = "node"
acceptable_keys = ("id", "status", "name", "cluster", "ip",
"mac", "roles", "pending_roles", "online")
"mac", "roles", "pending_roles", "online", "group_id")
def __init__(self):
super(NodeAction, self).__init__()
@@ -49,7 +49,7 @@ class NodeAction(Action):
Args.get_delete_from_db_arg(
"Delete specific nodes only from fuel db.\n"
"User should still delete node from cobbler"),
Args.get_provision_arg("Provision specific nodes.")
Args.get_provision_arg("Provision specific nodes."),
),
group(
Args.get_default_arg(

View File

@@ -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,
)
)

View File

@@ -337,6 +337,14 @@ def get_delete_arg(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):
return get_int_arg(
"release",

View File

@@ -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
)