To solve this optimization problem, we need to define the decision variables, the objective function, and the constraints.

Let's denote:
- $x_1$ as the number of ring layouts,
- $x_2$ as the number of tree layouts, and
- $x_3$ as the number of mesh layouts.

The objective is to maximize profit. The net profit for each type of layout is given as follows:
- Ring layout: $2000
- Tree layout: $4000
- Mesh layout: $8000

So, the objective function can be written as:
\[ \text{Maximize} \quad 2000x_1 + 4000x_2 + 8000x_3 \]

The constraints are based on the availability of workstations, servers, and switches:
- Workstations: $50x_1 + 30x_2 + 100x_3 \leq 2000$
- Servers: $20x_1 + 15x_2 + 50x_3 \leq 500$
- Switches: $10x_1 + 7x_2 + 30x_3 \leq 300$

Additionally, the number of each type of layout cannot be negative:
\[ x_1 \geq 0, x_2 \geq 0, x_3 \geq 0 \]

And since we can't have a fraction of a layout, ideally $x_1, x_2, x_3$ should be integers.

Here is the Gurobi code to solve this problem:

```python
from gurobipy import *

# Create a new model
m = Model("Nova_Network")

# Define decision variables
x1 = m.addVar(lb=0, vtype=GRB.INTEGER, name="ring_layouts")
x2 = m.addVar(lb=0, vtype=GRB.INTEGER, name="tree_layouts")
x3 = m.addVar(lb=0, vtype=GRB.INTEGER, name="mesh_layouts")

# Define the objective function
m.setObjective(2000*x1 + 4000*x2 + 8000*x3, GRB.MAXIMIZE)

# Add constraints
m.addConstr(50*x1 + 30*x2 + 100*x3 <= 2000, name="workstations")
m.addConstr(20*x1 + 15*x2 + 50*x3 <= 500, name="servers")
m.addConstr(10*x1 + 7*x2 + 30*x3 <= 300, name="switches")

# Optimize the model
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Ring layouts: {x1.x}")
    print(f"Tree layouts: {x2.x}")
    print(f"Mesh layouts: {x3.x}")
    print(f"Maximum profit: ${m.objVal}")
else:
    print("No optimal solution found")
```