Package: vstc
Changelist1.0.1,  update README1.0.0,  first releaseFeedbackgive your feedback by following waysvisithttps://github.com/dvdface/vstc(preferred)send email todvdface@gmail.comHow to installpip install vstcKnown issues1.x version, only support call locally. AKA, you can share a physical STC port between many test runers in the same PC.It will support RPC in 2.x version so that you can share a physical STC port between many PCs.OverviewVSTC means Virtual Spirent TestCenter.It takes Spirent TestCenter and Switch as a Virutal Spirent TestCenter.In general, you connect chassis, reserve port, create device under port, then create raw/bound streamblock, start traffic.With VSTC, it takes a STC port and Vlan ID as a virtual STC port so that the
traffics generated by a same STC port can be redirect to target port by switch with VLAN ID.Aka, one STC port can be used as  multiple virutal ports by this way. so, you can save your money to buy Spirent TestCenterOf course, you can use VSTC library without vid, so VSTC is just a ordinary STC.Class diagramThe relationship between classes is Just like Spirent TestCenter GUI.Chassis class provides ability to reserve port, create multicast groups.Port class provides ability to create devices, streamblocks.MCGroup represents Multicast Group, you can add IGMP type device into it.StreamBlock represents streamblock under Port.Device represents Device under Port.Impelmentation explainsUnder the hook, VSTC uses stc:: / sth:: api provided by Spirent TestCenter.VSTC uses STCObject to wrap object handle in stc::/sth::, so you can access / set object's attributes by PortObject['attribute'] syntax.In STCObject, it provides properties by which you can access attributes more easily.QA InfoName                 Stmts   Miss  Cover
----------------------------------------
vstc\__init__.py         9      0   100%
vstc\testcenter.py     821    116    86%
vstc\util.py            70      6    91%
----------------------------------------
TOTAL                  900    122    86%How to useChassis class# connect testcenter with ip 10.182.32.138 , without reserving any port
# creating chassis class will auto connect chassis
chassis = Chassis('10.182.32.138')

# disconnect chassis
chassis.disconnect()

# apply changes ( it will apply automatically)
chassis.apply()

# connect testcenter and reserve port
chassis = Chassis('10.182.32.138', [{ 'location' : '//10.182.32.138/1/1', 'vid': None}, { 'location' : '//10.182.32.138/1/2', 'vid': None}])

# connect testcenter, reserve port and specify a default vlan with specified vid
# when you create a device under the port, it will insert a vlan layer with vid 100 for you
# when you create a streamblock, it will insert a vlan layer with vid 100 for you too
chassis = Chassis('10.182.32.138', [{ 'location' : '//10.182.32.138/1/1', 'vid': 100}])

# save xml file
chassis.save('test_configuration.xml')

# get chassis serial number
chassis.serial

# get chassis ip
chassis.ipPort class# access reserved ports
# access all reserved ports
# it wil return List[Port]
chassis.ports

# access the first reserved ports
port = chassis.ports[0]

# start capture 
port.start_capture()
# stop capture
port.stop_capture()
# save capture
port.save_capture('demo.pcap')

# change DataPathMode
# normal: send / receive data to other port 
port.mode = PortMode.normal
# loopback: send data to self
port.mode = PortMode.local_loopback

# get port location
# //10.182.32.138/1/1
port.location
# get port vid
port.vid

# get port ownership state
# OwnershipState.RESERVED銆丄VAILABLE銆丏ISCONNECTED
port.ownership

# get port owner host
port.owner_host == socket.gethostname()

# get port onwer user
port.owner_userMCGroup class# access multicast groups under chassis
# it will return List[MCGroup]
chassis.mcgroups

# create a multicast group under chassis with name 'ipv4group', and group ip '224.0.0.1'
mcgroup = chassis.create_mcgroup('ipv4group', ip='224.0.0.1')

# remove mcgroup from chassis
chassis.remove_mcgroup(mcgroup)

# get / set group name
mcgroup.name
mcgroup.name = 'demo name'

# get / set group ip
mcgroup.ip
mcgroup.ip = '225.0.0.1'

# add IGMP device into MCGroup
# 1.get a reserved port under chassis
port = chassis.ports[0]
# 2. create igmp device under port
igmpDevice = port.create_device(DeviceType.igmp)
# 3. add igmp device into a mcgroup
mcgroup.add(igmpDevice)
# 4. remove igmp device from mcgroup
mcgroup.remove(igmpDevice)

# get devices in the mcgroup
# it will return List[IGMPDevice]
mcgroup.devicesDevice class# create traffic only device
dev = port.create_device(DeviceType.host)

# set device mac, ip, mask, gateway, gateway mac
dev.mac = '00:10:94:00:01:01'
dev.ip = '192.168.1.1'
dev.mask = 24
dev.gateway = '192.168.1.254'
dev.gwmac = '00:10:94:00:11:11'

# get stats for device
dev.stats

# create dhcp devices

dhcpv4server = port.create_device(DeviceType.dhcpv4_server, ip_address='192.168.10.1', ipaddress_pool='192.168.10.2')
dhcpv4client = port.create_device(DeviceType.dhcpv4_client)

# create igmp devices
igmp = port.create_device(DeviceType.igmp)

# create pppox devices
pppoxserver = port.create_device(DeviceType.pppox_server)
pppoxclient = port.create_device(DeviceType.pppox_client)

# set pppox auth mode, username, password and so on
pppoxserver.auth = PPPoXAuthMode.auto
pppoxserver.username = 'test'
pppoxserver.password = 'testpassword'Streamblock class# raw streamblock
streamblock = port.create_streamblock(StreamType.raw)

# bound streamblock
client = port.create_device(DeviceType.dhcpv4_client)
server = port.create_device(DeviceType.dhcpv4_server, ip_address='192.168.1.254', ipaddress_pool='192.168.1.1')

streamblock = port.create_streamblock(StreamType.bound, src_handle=client.handle, dst_handle=server.handle)

# start / stop streamblock
streamblock.start()

wait_for_true(lambda : streamblock.state == StreamState.running)

streamblock.stop()

# get / set streamblock speed
streamblock.speed
streamblock.speed = '10mbps'
streamblock.speed = '10pps'
streamblock.speed = '10%'

# get / set frame length
streamblock.length_mode
streamblock.min_length
streamblock.max_length
streamblock.length_step
streamblock.length_mode = LengthMode.incr
streamblock.min_length = 100
streamblock.max_length = 1500
streamblock.length_step = 64

# get / set fill type and fill constant
streamblock.fill_type
streamblock.fill_const
streamblock.fill_type = FillType.constant
streamblock.fill_const = 0x1234How to extendExtend Deviceclass NewDevice(Device):

   def _create_device(self, **kwargs) -> STCObject:
      # create device here by using stc:: or sth:: api
      dev_ret = SpirentAPI.instance.sth_emulation_device_config(mode='create', port_handle=self.port.handle, **kwargs)

      # wrap device object hanlde with STCObject
      return STCObject(dev_ret.handle)