====== Policy deployments, and the tags that drive them ====== How an edge node gets a whole site - network, storage, apps - without anyone naming that node anywhere. ===== What a deployment is ===== A **deployment** is a saved recipe for a site: the network to create, the storage to create, the apps to run. It lives inside a project. It never names an edge node. Instead, nodes //qualify// for it. That is the whole idea: you add a site by tagging hardware, not by editing a deployment project. **Watch out:** the project holding a deployment must be of type ''TAG_TYPE_DEPLOYMENT'' - not the ordinary project type. This is the most common setup mistake, and the error message points at the deployment rather than the project. ===== How a node qualifies ===== Two things must line up, and a third wires the app to its network. Each is a pair that must be //equal// on both sides - nothing matches by name. ==== First, membership ==== The edge node has to be moved **into the deployment's project**. A node belongs to exactly one project at a time, so this is a move, not an addition. Nothing below is even evaluated until this is true. ==== Match 1 - picks which deployment ==== ^ On the node ^ ^ On the deployment ^ | ''deployment_tag = cisco-policy-demo'' | must equal | ''deployment_tag = cisco-policy-demo'' | A project can hold more than one recipe. This says which one this node gets. ==== Match 2 - picks which policies ==== ^ On the node - tags ^ ^ On each policy - target condition ^ | ''demo = policy-demo'' | must equal | ''demo = policy-demo'' | One deployment can carry policies for several kinds of site. Each node receives only the ones whose condition its tags satisfy. ==== Match 3 - plumbing, never touched in a demo ==== ^ On the network policy - tags ^ ^ On the app's NIC - netinsttag ^ | ''demo = policy-demo-ni'' | must equal | ''demo = policy-demo-ni'' | The network does not exist until a node qualifies, so the app cannot refer to it by name. It finds it by tag instead. ==== The two words that trip people up ==== **Target condition.** A filter written on the //policy//, not on the node. It says: "only apply me to nodes whose tags include this exact key and value." The policy states what it is looking for, the node carries the labels, and the controller does the matching. A policy with no target condition applies to every node that reaches the deployment. **netinsttag.** The same idea one level down, and about networks instead of nodes. It is written on the app's network interface and says: "plug me into whichever network instance carries this tag." Why not just name the network? Because it does not exist yet. The network is created by a policy at the moment a node qualifies, and it is created separately for every node. There is no name to refer to when you are writing the config, so a label is the only stable handle. That is the pattern behind both: an object describes what it is looking for, and the controller finds the match when the node shows up. Nothing is wired by name. **Tip:** matches 2 and 3 both use the key ''demo'' with different values, which makes them look related when they are not. Renaming the network tag key to something like ''net'' makes it read unambiguously - ''demo='' always means nodes, ''net='' always means networks. Only matches 1 and 2 are set on the node. ===== What you actually do ===== - Move the node into the project. In ZedControl, change the node's project to the deployment project. - Set its deployment tag: ''deployment_tag = cisco-policy-demo'' - Set its tags: ''demo = policy-demo'' That is the entire action. No Terraform run, no touching the node. Within a minute or two the controller creates the network instance and the app instance, and the app comes up. Instances are named from the project, the app and the device, so two nodes under one deployment never collide: cisco-policy-demo.nginx.TF-CISCO-UCS-130C-M8-32 To drain a node, clear its deployment tag. To reset between runs, set it again. ===== Changing the recipe ===== **A deployment cannot be edited in place.** Terraform will report a successful apply and the controller will keep the old policies. Nothing warns you; the plan even goes clean afterwards. Any change to a deployment needs a destroy and recreate - and the delete is refused while a node is still attached, so unbind first: 1. clear the node's deployment_tag, apply 2. terraform apply -replace='...zedcloud_deployment.demo' 3. set the deployment_tag back, apply If you edited a deployment and nothing changed on the node, this is almost always why. ===== Nothing deployed - what to check ===== Every one of these fails //silently//. There is no error to read, so work down the list. * Is the project type ''TAG_TYPE_DEPLOYMENT''? * Is the node in that project - not its old one? * Does the node's ''deployment_tag'' match the deployment's, exactly? * Do the node's tags satisfy every policy's target condition? * Does the controller still hold policies you thought you deleted? * Does the app rely on a default network instance the node does not have? ==== Two red herrings ==== A node reading ''ADMIN_STATE_REGISTERED'' is normal - working nodes read that too. It does not mean the node is inactive. Testing a published port //from the node itself// always fails, because traffic originating on the host skips the rule that redirects it. Test from another machine. ---- Verified end to end on a Cisco UCS edge node: network instance, volume and nginx all created by policy, reachable on the node's published port. Terraform reference: ''cisco-ot-ign-demo-policy-based/'' - see ''README-full.md'' for API queries and the full history.