Skip to content

Commit

Permalink
CZ-98 Add support for lookup the network for vm based on input provid… (
Browse files Browse the repository at this point in the history
#8)

* CZ-98 Add support for lookup the network for vm based on input provided by user

* CZ-98 Update logging message

* CZ-98 Fix some issues and add support ip version checking

* CZ-98 Update networks

* CZ-98 Fix grapping ports for vifs

* CZ-98 Update the fetching network process for nsxt server

* CZ-98 Update network name to use id instead

* CZ-98 Update Readme file
  • Loading branch information
mabuaisha authored Aug 25, 2020
1 parent a95caf7 commit 8efd1c8
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 16 deletions.
106 changes: 104 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ The plugin provides the following features for interacting with NSX-T API:
- Create Tier1 Gateway
- Delete Tier1 Gateway

4. Virtual Machine:
- List Virtual Machines
- List All Virtual Network Interface Associated with VM

## Authentication with NSX-T

Each node template, has a `client_config` property which stores your account credentials.
Expand Down Expand Up @@ -156,7 +160,6 @@ This node type refers to a Segment.
* `cloudify.relationships.nsx-t.segment_connected_to_dhcp_server_config`:
* `cloudify.nodes.nsx-t.DhcpServerConfig`: Depend on DHCP Server Config


### Segment Example

```yaml
Expand Down Expand Up @@ -224,7 +227,6 @@ This node type refers to a Tier1 Gateway.
* `children`: _List_: subtree for this type within policy tree containing nested elements.
* `tags`: _List_: Opaque identifiers meaningful to the API user


### Tier1 Example

```yaml
Expand All @@ -243,7 +245,107 @@ This node type refers to a Tier1 Gateway.
tier0_path:{ get_input: tier0_path }
```

### **cloudify.types.nsx-t.inventory.VirtualMachine**

This node type refers to a Virtual Machine resource.

**Resource Config**
* `vm_id`: _String_. _Not required_. External VM ID.
* `vm_name`: _String_. _Not required_. The Name of VM.
* `network_id`: _String_. _Required_. The network id to get ips from.

## Runtime Properties

Beside the common runtime properties, the `VirtualMachine` node type also expose the following two runtime properties:
* `networks`: _Dict_. Dictionary of all virtual network interfaces attached to the current vm.

```json
{
"Network adapter 1":{
"device_key":"4000",
"device_name":"Network adapter 1",
"external_id":"502a627a-2b5a-0f27-ace9-44a9b66d9692-4000",
"host_id":"55174c3a-412e-4083-b2f1-cf2cd265ef5b",
"ip_address_info":[
{
"ip_addresses":[
"192.168.11.100",
"2001:ab8::250:56ff:feaa:9160",
"fe80::250:56ff:feaa:9160"
],
"source":"VM_TOOLS"
}
],
"lport_attachment_id":"3e660c66-7a8e-45a4-8c82-ffe3afcc7de2",
"mac_address":"00:50:56:aa:91:60",
"owner_vm_id":"502a627a-2b5a-0f27-ace9-44a9b66d9692",
"owner_vm_type":"REGULAR",
"vm_local_id_on_host":"49",
"_last_sync_time":1598369175580,
"display_name":"Network adapter 1",
"resource_type":"VirtualNetworkInterface",
"ipv4_addresses":[
"192.168.11.100"
],
"ipv6_addresses":[
"2001:ab8::250:56ff:feaa:9160",
"fe80::250:56ff:feaa:9160"
]
}
}
```

* The value of `network_id` is the exposed as runtime property with the following value:
```json
{
"device_key":"4001",
"device_name":"Network adapter 2",
"external_id":"502a479c-e3f4-2ace-daab-4d874e8cc8b6-4001",
"host_id":"55174c3a-412e-4083-b2f1-cf2cd265ef5b",
"ip_address_info":[
{
"ip_addresses":[
"192.168.234.100",
"fe80::250:56ff:feaa:34fd"
],
"source":"VM_TOOLS"
}
],
"lport_attachment_id":"a9e26ccb-eb63-4098-bb7d-a93b6257b33f",
"mac_address":"00:50:56:aa:34:fd",
"owner_vm_id":"502a479c-e3f4-2ace-daab-4d874e8cc8b6",
"owner_vm_type":"REGULAR",
"vm_local_id_on_host":"22",
"_last_sync_time":1597825357509,
"display_name":"Network adapter 2",
"resource_type":"VirtualNetworkInterface"
}
```

### VirtualMachine Example

```yaml
virtual_machine_inventory:
type: cloudify.nodes.nsx-t.inventory.VirtualMachine
properties:
client_config:
host: { get_input: host }
port: { get_input: port }
username: { get_input: username }
password: { get_input: password }
resource_config:
vm_name: { get_attribute: [ host, name ] }
network_id: { get_attribute: [ segment, id ] }
relationships:
- type: cloudify.relationships.nsx-t.inventory_connected_to_server
target: host
- type: cloudify.relationships.depends_on
target: segment
```

Note: The configuration for the above resources are based on the NSX-T API documentation:
1. [Segment 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_segment.html)
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)
27 changes: 27 additions & 0 deletions examples/blueprint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,30 @@ node_templates:
relationships:
- type: cloudify.relationships.depends_on
target: segment

virtual_machine_inventory:
type: cloudify.nodes.nsx-t.inventory.VirtualMachine
properties:
client_config: *client_config
resource_config:
vm_name: { get_attribute: [ host, name ] }
network_id: { get_attribute: [ segment, id ] }
relationships:
- type: cloudify.relationships.nsx-t.inventory_connected_to_server
target: host

outputs:
target_network:
value: { get_attribute: [ virtual_machine_inventory, { get_attribute: [ segment, id ] } ] }

networks:
value: { get_attribute: [ virtual_machine_inventory, networks ] }

server_mac_address:
value: { get_attribute: [ virtual_machine_inventory, { get_attribute: [ segment, id ] }, mac_address ] }

server_ipv4_address:
value: { get_attribute: [ virtual_machine_inventory, { get_attribute: [ segment, id ] }, ipv4_addresses, 0 ] }

server_ipv6_address:
value: { get_attribute: [ virtual_machine_inventory, { get_attribute: [ segment, id ] }, ipv6_addresses, 0 ] }
2 changes: 1 addition & 1 deletion nsx_t_plugin/dhcp_server/dhcp_server_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _update_tier_1_gateway(client_config, tier1_gateway_id, dhcp_server_paths):
},
logger=ctx.logger
)
tier1_object = tier1.get()
tier1_object = tier1.get(to_dict=False)
tier1_object.dhcp_config_paths = dhcp_server_paths
tier1.update(tier1_object)

Expand Down
12 changes: 8 additions & 4 deletions nsx_t_plugin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,16 @@ def set_basic_runtime_properties_for_instance(nsx_t_resource, _ctx):
RelationshipSubjectContext or CloudifyContext
"""
if _ctx and nsx_t_resource:
resource_config = nsx_t_resource.get().to_dict()
resource_config = nsx_t_resource.get()
_ctx.instance.runtime_properties[
NSXT_TYPE_PROPERTY] = nsx_t_resource.resource_type
_ctx.instance.runtime_properties[
NSXT_ID_PROPERTY] = nsx_t_resource.resource_id
_ctx.instance.runtime_properties[
NSXT_NAME_PROPERTY] = resource_config['display_name']
NSXT_NAME_PROPERTY] = \
resource_config.get('display_name') or\
resource_config.get('id') or\
nsx_t_resource.resource_id
_ctx.instance.runtime_properties[
NSXT_RESOURCE_CONFIG_PROPERTY] = resource_config

Expand Down Expand Up @@ -171,8 +174,9 @@ def validate_if_resource_started(
:param ready_states: List of ready states to say that resource is ready
"""
resource_state = nsx_t_state.get()
state = getattr(resource_state, nsx_t_state.state_attr, 'state')
state = state.state if hasattr(state, 'state') else state
state = resource_state[nsx_t_state.state_attr]
if isinstance(state, dict):
state = state['state']
if state in pending_states:
raise OperationRetry(
'{0} state '
Expand Down
Empty file.
135 changes: 135 additions & 0 deletions nsx_t_plugin/virtual_machine/virtual_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
########
# Copyright (c) 2020 Cloudify Technologies Ltd. All rights reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.

from IPy import IP

from cloudify import ctx
from cloudify.exceptions import NonRecoverableError

from nsx_t_plugin.decorators import with_nsx_t_client
from nsx_t_sdk.resources import (
VirtualMachine,
VirtualNetworkInterface,
SegmentPort
)


def _update_network_with_ipv4_and_ipv6(network):
ipv_4 = []
ipv_6 = []
ip_address_info = network.get('ip_address_info', []) or []
for ip_address_obj in ip_address_info:
ip_addresses = ip_address_obj.get('ip_addresses', []) or []
for ip_address in ip_addresses:
if IP(ip_address).version() == 4:
ipv_4.append(ip_address)
elif IP(ip_address).version() == 6:
ipv_6.append(ip_address)

network['ipv4_addresses'] = ipv_4
network['ipv6_addresses'] = ipv_6


def _lookup_segment_ports(client_config, network_name):
segment_port = SegmentPort(
client_config=client_config,
logger=ctx.logger,
resource_config={}
)
ports = []
for nsx_t_port in segment_port.list(
filters={
'segment_id': network_name
}
):
if nsx_t_port.get('attachment'):
ports.append(nsx_t_port['attachment']['id'])

return ports


def _get_target_network(ports, network):
if not network.get('lport_attachment_id'):
return {}
return network if network['lport_attachment_id'] in ports else {}


def _populate_networks_for_virtual_machine(
client_config,
owner_vm_id,
network_id,
networks
):
ports = _lookup_segment_ports(client_config, network_id)
if not ports:
raise NonRecoverableError('Network {0} is not connected to any device')
networks_obj = {}
networks_obj['networks'] = {}
target_network = {}
for network in networks:
_update_network_with_ipv4_and_ipv6(network)
if not target_network:
target_network = _get_target_network(ports, network)
networks_obj['networks'][network['display_name']] = network

if not target_network:
raise NonRecoverableError(
'The selected network {0} is not '
'attached to target virtual machine {1}'
''.format(network_id, owner_vm_id)
)

ctx.instance.runtime_properties[network_id] = target_network
ctx.instance.runtime_properties['networks'] = networks_obj


@with_nsx_t_client(VirtualMachine)
def create(nsx_t_resource):
network_id = nsx_t_resource.resource_config.get('network_id')
if not network_id:
raise NonRecoverableError(
'Network name is required in order '
'to fetch the network interface attached to target '
'virtual machine'
)
ctx.logger.info(
'Preparing resource to fetch target network interface'
' {0} for target virtual machine'
''.format(network_id)
)


@with_nsx_t_client(VirtualNetworkInterface)
def configure(nsx_t_resource):
owner_vm_id = ctx.instance.runtime_properties.get('id')
if not owner_vm_id:
owner_vm_id = nsx_t_resource.resource_id
filters = {
'owner_vm_id': owner_vm_id
}
# This will list all networks interface attached to specific vm
networks = nsx_t_resource.list(filters=filters)
network_id = nsx_t_resource.resource_config.get('network_id')
if not networks:
raise NonRecoverableError(
'Virtual Machine is not attached to any '
'network'
)
_populate_networks_for_virtual_machine(
nsx_t_resource.client_config,
owner_vm_id,
network_id,
networks
)
12 changes: 9 additions & 3 deletions nsx_t_sdk/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from com.vmware import nsx_policy_client
from com.vmware import nsx_client
from com.vmware.nsx_policy import infra_client
from com.vmware.nsx import fabric_client
from com.vmware.nsx_policy.infra import segments_client, tier_1s_client

from nsx_t_sdk import exceptions
Expand Down Expand Up @@ -57,7 +58,9 @@ def __init__(self, client_config, resource_config, logger):
self.client_config = client_config
self.logger = logger
self.resource_config = resource_config or {}
self.resource_config['resource_type'] = self.resource_type
# Only populate resource_type if it is set
if self.resource_type:
self.resource_config['resource_type'] = self.resource_type
self.resource_id = self.resource_config.pop('id', None)
self._api_client = self._prepare_nsx_t_client()

Expand All @@ -68,7 +71,8 @@ def _get_nsx_client_map():
'nsx_policy': nsx_policy_client,
'nsx_infra': infra_client,
'segment': segments_client,
'tier_1': tier_1s_client
'tier_1': tier_1s_client,
'fabric': fabric_client
}

def _get_stub_factory_for_nsx_client(self, stub_config):
Expand Down Expand Up @@ -212,8 +216,10 @@ def delete(self, extra_params=None):

return self._invoke(ACTION_DELETE, params)

def get(self):
def get(self, to_dict=True):
self._validate_allowed_method(self.allow_get, ACTION_GET)
if to_dict:
return self._invoke(ACTION_GET, (self.resource_id,)).to_dict()
return self._invoke(ACTION_GET, (self.resource_id,))

def list(self,
Expand Down
Loading

0 comments on commit 8efd1c8

Please sign in to comment.