Skip to content

Commit

Permalink
Cz 97 use static binding between segment and server (#9)
Browse files Browse the repository at this point in the history
* CZ-97 Add support for static bindings between server and segments

* CZ-97 Set unique id for segment instance

* CZ-97 Bump version to 0.3.1

* CZ-97 Fix issue with plugin sdk and static bindings

* Add support for dhcp status binding

* CZ-97 Fix flake8 issue

* CZ-97 Update static binding code

* CZ-97 Remove unused imports

* CZ-97 Fix some issues

* CZ-97 Fix issue with creating dhcp binding settings and handling errors

* CZ-97 Fix some issues for plugin

* CZ-97 Update resource deletion process

* CZ-97 Update handling state for static binding

* CZ-97 Update deletion process for static bindings

* CZ-97 Add some fixes

* CZ-97 Update the segment code
  • Loading branch information
mabuaisha authored Aug 27, 2020
1 parent 8efd1c8 commit b63a69b
Show file tree
Hide file tree
Showing 11 changed files with 377 additions and 87 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,4 @@ Note: The configuration for the above resources are based on the NSX-T API docum
2. [DHCP Server Endpoints](https://vdc-download.vmware.com/vmwb-repository/dcr-public/9e1c6bcc-85db-46b6-bc38-d6d2431e7c17/30af91b5-3a91-4d5d-8ed5-a7d806764a16/api_includes/policy_networking_ip_management_dhcp_dhcp_server_configs.html)
3. [Tier1 Endpoints](https://vdc-download.vmware.com/vmwb-repository/dcr-public/9e1c6bcc-85db-46b6-bc38-d6d2431e7c17/30af91b5-3a91-4d5d-8ed5-a7d806764a16/api_includes/policy_networking_connectivity_tier-1_gateways_tier-1_gateways.html)
4. [Virtual Interface Endpoints](https://vdc-download.vmware.com/vmwb-repository/dcr-public/9e1c6bcc-85db-46b6-bc38-d6d2431e7c17/30af91b5-3a91-4d5d-8ed5-a7d806764a16/api_includes/system_administration_configuration_fabric_inventory_virtual_interfaces.html)
4. [Virtual Machines Endpoints](https://vdc-download.vmware.com/vmwb-repository/dcr-public/9e1c6bcc-85db-46b6-bc38-d6d2431e7c17/30af91b5-3a91-4d5d-8ed5-a7d806764a16/api_includes/method_ListVirtualMachines.html)
5. [Virtual Machines Endpoints](https://vdc-download.vmware.com/vmwb-repository/dcr-public/9e1c6bcc-85db-46b6-bc38-d6d2431e7c17/30af91b5-3a91-4d5d-8ed5-a7d806764a16/api_includes/method_ListVirtualMachines.html)
11 changes: 10 additions & 1 deletion examples/blueprint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ inputs:
type: string
default: Cluster

ip_address:
type: string

dsl_definitions:

client_config: &client_config
Expand Down Expand Up @@ -111,8 +114,14 @@ node_templates:
nsx_t_switch: { get_input: nsx_t_switch }
use_dhcp: true
relationships:
- type: cloudify.relationships.depends_on
- type: cloudify.relationships.server_connected_to_segment
target: segment
target_interfaces:
cloudify.interfaces.relationship_lifecycle:
preconfigure:
inputs:
network_unique_id: { get_attribute: [ segment, unique_id ] }
ip_address: { get_input: ip_address }

virtual_machine_inventory:
type: cloudify.nodes.nsx-t.inventory.VirtualMachine
Expand Down
2 changes: 1 addition & 1 deletion nsx_t_plugin/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def _decorator(func):
@wraps(func)
def wrapper(**kwargs):
_ctx = kwargs.pop('ctx', CloudifyContext)
_ctx = get_ctx_object(_ctx)
operation_name = _ctx.operation.name
_ctx = get_ctx_object(_ctx)
kwargs['nsx_t_resource'] = populate_nsx_t_instance_from_ctx(
class_decl,
_ctx,
Expand Down
4 changes: 2 additions & 2 deletions nsx_t_plugin/dhcp_server/dhcp_server_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _update_tier_1_gateway(client_config, tier1_gateway_id, dhcp_server_paths):
)
tier1_object = tier1.get(to_dict=False)
tier1_object.dhcp_config_paths = dhcp_server_paths
tier1.update(tier1_object)
tier1.update(tier1_gateway_id, tier1_object)


def _link_dhcp_server_to_tier_1(client_config, tier1_gateway_id):
Expand Down Expand Up @@ -85,4 +85,4 @@ def stop(nsx_t_resource):

@with_nsx_t_client(DhcpServerConfig)
def delete(nsx_t_resource):
nsx_t_resource.delete()
nsx_t_resource.delete(nsx_t_resource.resource_id)
265 changes: 249 additions & 16 deletions nsx_t_plugin/segment/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
# * See the License for the specific language governing permissions and
# * limitations under the License.

import time

from cloudify import ctx
from cloudify.exceptions import NonRecoverableError

from nsx_t_plugin.decorators import with_nsx_t_client
from nsx_t_plugin.constants import (
STATE_IN_PROGRESS,
STATE_SUCCESS,
STATE_PENDING,
STATE_IN_SYNC
)
from nsx_t_plugin.utils import (
validate_if_resource_started,
Expand All @@ -28,7 +32,9 @@
from nsx_t_sdk.resources import (
Segment,
SegmentState,
SegmentPort
DhcpV4StaticBindingConfig,
DhcpV6StaticBindingConfig,
DhcpStaticBindingState
)


Expand All @@ -42,6 +48,167 @@ def _update_subnet_configuration(resource_config):
resource_config['subnets'].append(ip_option_config)


def _get_networks_info_from_inputs(
network_unique_id,
ip_v4_address,
ip_v6_address
):
if not network_unique_id:
network_unique_id = ctx.target.instance.runtime_properties.get(
'unique_id'
)
if not any([ip_v4_address, ip_v6_address]):
raise NonRecoverableError(
'`ip_v4_address` & `ip_v6_address` cannot be both unset, '
'select at least on of them'
)
server_networks = ctx.source.instance.runtime_properties.get('networks')
server_id = ctx.source.instance.runtime_properties.get('id')
if not server_id:
server_id = ctx.source.instance.id
if not server_networks:
raise NonRecoverableError(
'`networks` runtime property is either not set or empty for '
'server {0}'.format(server_id))
return network_unique_id, server_networks


def _prepare_dhcp_static_binding_configs(
segment_id,
mac_address,
ipv4_address,
ip_v6_address
):
dhcp_v4_config = {}
dhcp_v6_config = {}

if ipv4_address:
dhcp_v4_config['id'] = '{segment}-{dhcp}'.format(
segment=segment_id, dhcp='dhcpv4'
)
dhcp_v4_config['ip_address'] = ipv4_address
dhcp_v4_config['mac_address'] = mac_address

if ip_v6_address:
dhcp_v6_config['id'] = '{segment}-{dhcp}'.format(
segment=segment_id, dhcp='dhcpv6'
)
dhcp_v6_config['ip_addresses'] = [ip_v6_address]
dhcp_v6_config['mac_address'] = mac_address

return dhcp_v4_config, dhcp_v6_config


def _handle_dhcp_static_bindings(
class_type,
dhcp_type,
segment_id,
client_config,
dhcp_config):
tasks = ctx.target.instance.runtime_properties.setdefault('tasks', {})
if dhcp_config:
dhcp_binding = class_type(
client_config=client_config,
logger=ctx.logger,
resource_config=dhcp_config)
if not tasks.get(dhcp_binding.resource_id):
dhcp_binding_response = dhcp_binding.update(
segment_id,
dhcp_binding.resource_id,
dhcp_config
)
tasks[dhcp_binding.resource_id] = True
ctx.target.instance.runtime_properties[
'dhcp_{0}_static_binding_id'.format(dhcp_type)] = \
dhcp_binding.resource_id
ctx.target.instance.runtime_properties[
'dhcp_{0}_static_binding'.format(dhcp_type)] = \
dhcp_binding_response.to_dict()

ctx.logger.info(
'Waiting 30 seconds before get'
' the state of the bindings'
)
time.sleep(30)
static_state = DhcpStaticBindingState(
client_config=client_config,
resource_config={},
logger=ctx.logger
)
validate_if_resource_started(
'DhcpStaticBinding',
static_state,
[STATE_PENDING, STATE_IN_PROGRESS],
[STATE_SUCCESS, STATE_IN_SYNC],
args=(segment_id, dhcp_binding.resource_id,)
)


def _create_dhcp_static_binding_configs(
segment_id,
client_config,
dhcp_v4_config,
dhcp_v6_config
):
_handle_dhcp_static_bindings(
DhcpV4StaticBindingConfig,
'v4',
segment_id,
client_config,
dhcp_v4_config
)
_handle_dhcp_static_bindings(
DhcpV6StaticBindingConfig,
'v6',
segment_id,
client_config,
dhcp_v6_config
)


def _wait_on_dhcp_static_bindings(segment_id, client_config):
dhcp_v4_binding = DhcpV4StaticBindingConfig(
client_config=client_config,
resource_config={},
logger=ctx.logger
)

dhcp_v6_binding = DhcpV4StaticBindingConfig(
client_config=client_config,
resource_config={},
logger=ctx.logger
)

bindings_v4 = []
bindings_v6 = []
for static_v4_bindings in dhcp_v4_binding.list(
filters={
'segment_id': segment_id
}
):
bindings_v4.append(static_v4_bindings['id'])

for dhcp_v6_binding in dhcp_v6_binding.list(
filters={
'segment_id': segment_id
}
):
bindings_v6.append(dhcp_v6_binding['id'])

if not any([bindings_v4, bindings_v6]):
ctx.logger.info('All DHCP Static Binding objects are deleted')
else:
for binding_v4_id in bindings_v4:
validate_if_resource_deleted(
dhcp_v4_binding, (segment_id, binding_v4_id,)
)

for binding_v6_id in bindings_v6:
validate_if_resource_deleted(
dhcp_v6_binding, (segment_id, binding_v6_id,)
)


@with_nsx_t_client(Segment)
def create(nsx_t_resource):
# Update the subnet configuration for segment
Expand All @@ -51,6 +218,9 @@ def create(nsx_t_resource):
# Update the resource_id with the new "id" returned from API
nsx_t_resource.resource_id = resource.id

# Set the unique_id to use it later on
ctx.instance.runtime_properties['unique_id'] = resource.unique_id


@with_nsx_t_client(SegmentState)
def start(nsx_t_resource):
Expand All @@ -64,24 +234,87 @@ def start(nsx_t_resource):

@with_nsx_t_client(Segment)
def stop(nsx_t_resource):
segment_port = SegmentPort(
client_config=nsx_t_resource.client_config,
logger=ctx.logger,
resource_config={}
_wait_on_dhcp_static_bindings(
nsx_t_resource.resource_id,
nsx_t_resource.client_config
)
for nsx_t_port in segment_port.list(
filters={
'segment_id': nsx_t_resource.resource_id
}
):
port = SegmentPort(
client_config=nsx_t_resource.client_config,
logger=ctx.logger,
resource_config={'id': nsx_t_port['id']}
)
port.delete((nsx_t_resource.resource_id,))


@with_nsx_t_client(Segment)
def delete(nsx_t_resource):
validate_if_resource_deleted(nsx_t_resource)


@with_nsx_t_client(Segment)
def add_static_bindings(
nsx_t_resource,
network_unique_id,
ip_v4_address,
ip_v6_address
):
network_unique_id, networks = _get_networks_info_from_inputs(
network_unique_id, ip_v4_address, ip_v6_address
)
for network in networks:
if network.get('name') == network_unique_id:
mac_address = network.get('mac')
break
else:
raise NonRecoverableError(
'Network {0} is not attached to server. Select a valid '
'network'.format(network_unique_id)
)
if not mac_address:
raise NonRecoverableError(
'Mac address cannot be empty '
'for network {0}'.format(network_unique_id)
)
dhcp_v4_config, dhcp_v6_config = _prepare_dhcp_static_binding_configs(
nsx_t_resource.resource_id,
mac_address,
ip_v4_address,
ip_v6_address
)
_create_dhcp_static_binding_configs(
nsx_t_resource.resource_id,
nsx_t_resource.client_config,
dhcp_v4_config,
dhcp_v6_config
)


@with_nsx_t_client(Segment)
def remove_static_bindings(nsx_t_resource):
dhcp_v4_static_binding_id = ctx.target.instance.runtime_properties.get(
'dhcp_v4_static_binding_id'
)
dhcp_v6_static_binding_id = ctx.target.instance.runtime_properties.get(
'dhcp_v6_static_binding_id'
)
if not any([dhcp_v4_static_binding_id, dhcp_v6_static_binding_id]):
raise NonRecoverableError(
'DHCP Static binding ids are not set for '
'ipv4 & ipv6, at least one must be set as '
'runtime property'
)
if dhcp_v4_static_binding_id:
dhcp_v4_binding = DhcpV4StaticBindingConfig(
client_config=nsx_t_resource.client_config,
resource_config={'id': dhcp_v4_static_binding_id},
logger=ctx.logger
)
dhcp_v4_binding.delete(
nsx_t_resource.resource_id,
dhcp_v4_static_binding_id
)

if dhcp_v6_static_binding_id:
dhcp_v6_binding = DhcpV6StaticBindingConfig(
client_config=nsx_t_resource.client_config,
resource_config={'id': dhcp_v6_static_binding_id},
logger=ctx.logger
)
dhcp_v6_binding.delete(
nsx_t_resource.resource_id,
dhcp_v6_static_binding_id
)
Loading

0 comments on commit b63a69b

Please sign in to comment.